package flare.data
{
/**
* A data table maintains a collection of data objects, each
* representing a row of data, and an optional data schema describing
* the data variables.
*/
public class DataTable
{
/**
* Creates a new data table instance.
* @param data an array of tuples, each tuple is a row of data
* @param schema an optional DataSchema describing the data columns
*/
public function DataTable(data:Array, schema:DataSchema=null) {
this.data = data;
this.schema = schema;
}
/** A DataSchema describing the data columns of the table. */
public var schema:DataSchema;
/** An array of data objects, each representing a row of data. */
public var data:Array;
} // end of class DataTable
}
Friday, January 1, 2010
DataSet
package flare.data
{
/**
* A data set is a collection of data tables.
*/
public class DataSet
{
/**
* Creates a new DataSet.
* @param nodes a data table of node data
* @param edges a data table of edge data (optional, for graphs only)
*/
public function DataSet(nodes:DataTable, edges:DataTable=null) {
this.nodes = nodes;
this.edges = edges;
}
/** A DataTable of nodes (or table rows). */
public var nodes:DataTable = null;
/** A DataTable of edges. */
public var edges:DataTable = null;
} // end of class DataSet
}
{
/**
* A data set is a collection of data tables.
*/
public class DataSet
{
/**
* Creates a new DataSet.
* @param nodes a data table of node data
* @param edges a data table of edge data (optional, for graphs only)
*/
public function DataSet(nodes:DataTable, edges:DataTable=null) {
this.nodes = nodes;
this.edges = edges;
}
/** A DataTable of nodes (or table rows). */
public var nodes:DataTable = null;
/** A DataTable of edges. */
public var edges:DataTable = null;
} // end of class DataSet
}
Classes
flare->data->DataSet
flare->data->DataTable
flare->vis->data->render->EdgeRenderer
flare->vis->data->EdgeSprite
flare->vis->data->NodeSprite
flare->data->DataTable
flare->vis->data->render->EdgeRenderer
flare->vis->data->EdgeSprite
flare->vis->data->NodeSprite
Edge Sprite
package flare.vis.data
{
import flare.vis.data.render.ArrowType;
import flare.vis.data.render.EdgeRenderer;
/**
* Visually represents a connection between two data elements. Examples
* include an edge in a graph structure or a line between points in a line
* chart. EdgeSprites maintain
* properties for accessing the NodeSprites connected by this edge. By
* default, EdgeSprites are drawn using an
* EdgeSprites are typically managed by a
*/
public class EdgeSprite extends DataSprite
{
// -- Properties ------------------------------------------------------
/** The x-coordinate for the first end point of this edge. */
public var x1:Number;
/** The y-coordinate for the first end point of this edge. */
public var y1:Number;
/** The x-coordinate for the second end point of this edge. */
public var x2:Number;
/** The y-coordinate for the second end point of this edge. */
public var y2:Number;
/** The first, or source, node upon which this edge is incident. */
public var source:NodeSprite;
/** The second, or target, node upon which this edge is incident. */
public var target:NodeSprite;
/** Flag indicating if this edge is directed (true) or undirected
* (false). */
public var directed:Boolean = false;
/** The type of arrow to be used on the edge. Default is Arrows.NONE */
public var arrowType:String = ArrowType.NONE;
/** The width of the arrow head. The default is -1, in which case the
* width is automatically determined based on the arrow height or
* the line width. */
public var arrowWidth:Number = -1;
/** The height of the arrow head. The default is -1, in which case the
* height is automatically determined based on the arrow width or
* the line width. */
public var arrowHeight:Number = -1;
// -- Methods ---------------------------------------------------------
/**
* Creates a new EdgeSprite.
* @param source the source node
* @param target the target node
* @param directed true for a directed edge, false for undirected
*/
public function EdgeSprite(source:NodeSprite=null,
target:NodeSprite=null, directed:Boolean=false)
{
this.source = source;
this.target = target;
this.directed = directed;
_lineColor = 0xffcccccc;
_renderer = EdgeRenderer.instance;
}
/**
* Given a node upon which this edge is incident, return the other
* node connected by this edge.
* @param n a node upon which this edge is incident
* @return the other node
*/
public function other(n:NodeSprite):NodeSprite
{
if (n == source) return target;
if (n == target) return source;
else return null;
}
/**
* Clears the edge, removing references to the edge's nodes.
*/
public function clear():void
{
source = null;
target = null;
}
/** @inheritDoc */
public override function render():void
{
if (source != null) {
x1 = source.x;
y1 = source.y;
}
if (target != null) {
x2 = target.x;
y2 = target.y;
}
super.render();
}
} // end of class EdgeSprite
}
{
import flare.vis.data.render.ArrowType;
import flare.vis.data.render.EdgeRenderer;
/**
* Visually represents a connection between two data elements. Examples
* include an edge in a graph structure or a line between points in a line
* chart. EdgeSprites maintain
source
and target
* properties for accessing the NodeSprites connected by this edge. By
* default, EdgeSprites are drawn using an
EdgeRenderer
.* EdgeSprites are typically managed by a
Data
object.*/
public class EdgeSprite extends DataSprite
{
// -- Properties ------------------------------------------------------
/** The x-coordinate for the first end point of this edge. */
public var x1:Number;
/** The y-coordinate for the first end point of this edge. */
public var y1:Number;
/** The x-coordinate for the second end point of this edge. */
public var x2:Number;
/** The y-coordinate for the second end point of this edge. */
public var y2:Number;
/** The first, or source, node upon which this edge is incident. */
public var source:NodeSprite;
/** The second, or target, node upon which this edge is incident. */
public var target:NodeSprite;
/** Flag indicating if this edge is directed (true) or undirected
* (false). */
public var directed:Boolean = false;
/** The type of arrow to be used on the edge. Default is Arrows.NONE */
public var arrowType:String = ArrowType.NONE;
/** The width of the arrow head. The default is -1, in which case the
* width is automatically determined based on the arrow height or
* the line width. */
public var arrowWidth:Number = -1;
/** The height of the arrow head. The default is -1, in which case the
* height is automatically determined based on the arrow width or
* the line width. */
public var arrowHeight:Number = -1;
// -- Methods ---------------------------------------------------------
/**
* Creates a new EdgeSprite.
* @param source the source node
* @param target the target node
* @param directed true for a directed edge, false for undirected
*/
public function EdgeSprite(source:NodeSprite=null,
target:NodeSprite=null, directed:Boolean=false)
{
this.source = source;
this.target = target;
this.directed = directed;
_lineColor = 0xffcccccc;
_renderer = EdgeRenderer.instance;
}
/**
* Given a node upon which this edge is incident, return the other
* node connected by this edge.
* @param n a node upon which this edge is incident
* @return the other node
*/
public function other(n:NodeSprite):NodeSprite
{
if (n == source) return target;
if (n == target) return source;
else return null;
}
/**
* Clears the edge, removing references to the edge's nodes.
*/
public function clear():void
{
source = null;
target = null;
}
/** @inheritDoc */
public override function render():void
{
if (source != null) {
x1 = source.x;
y1 = source.y;
}
if (target != null) {
x2 = target.x;
y2 = target.y;
}
super.render();
}
} // end of class EdgeSprite
}
NodeSprite
package flare.vis.data
{
import flare.animate.Transitioner;
import flare.util.Arrays;
import flare.util.Filter;
import flare.util.IEvaluable;
import flare.util.Sort;
/**
* Visually represents a data element, such as a data tuple or graph node.
* By default, NodeSprites are drawn using a
NodeSprites can separately maintain adjacency lists for both a
* general graph structure (managing lists for inlinks and outlinks) and a
* tree structure (managing a list for child links and a parent pointer).
* The graph and tree lists are maintained completely separately to
* maximize flexibility. While the the tree lists are often used to record
* a spanning tree of the general network structure, they can also be used
* to represent a hierarchy completely distinct from a co-existing graph
* structure. Take this into account when iterating over the edges incident
* on this node.
*/
public class NodeSprite extends DataSprite
{
/** Flag indicating inlinks, edges that point to this node. */
public static const IN_LINKS:uint = 1;
/** Flag indicating outlinks, edges that point away from node. */
public static const OUT_LINKS:uint = 2;
/** Flag indicating both inlinks and outlinks. */
public static const GRAPH_LINKS:uint = 3; // IN_LINKS | OUT_LINKS
/** Flag indicating child links in a tree structure. */
public static const CHILD_LINKS:uint = 4;
/** Flag indicating the link to a parent in a tree structure. */
public static const PARENT_LINK:uint = 8;
/** Flag indicating both child and parent links. */
public static const TREE_LINKS:uint = 12; // CHILD_LINKS | PARENT_LINK
/** Flag indicating all links, including graph and tree links. */
public static const ALL_LINKS:uint = 15; // GRAPH_LINKS | TREE_LINKS
/** Flag indicating that a traversal should be performed in reverse. */
public static const REVERSE:uint = 16;
// -- Properties ------------------------------------------------------
private var _parentEdge:EdgeSprite;
private var _idx:int = -1; // node index in parent's array
private var _childEdges:/*EdgeSprite*/Array;
private var _inEdges:/*EdgeSprite*/Array;
private var _outEdges:/*EdgeSprite*/Array;
private var _expanded:Boolean = true;
/** Flag indicating if this node is currently expanded. This flag can
* be used by layout routines to expand/collapse connections. */
public function get expanded():Boolean { return _expanded; }
public function set expanded(b:Boolean):void { _expanded = b; }
/** The edge connecting this node to its parent in a tree structure. */
public function get parentEdge():EdgeSprite { return _parentEdge; }
public function set parentEdge(e:EdgeSprite):void { _parentEdge = e; }
/** The index of this node in its tree parent's child links list. */
public function get parentIndex():int { return _idx; }
public function set parentIndex(i:int):void { _idx = i; }
// -- Node Degree Properties ------------------------------------------
/** The number of child links. */
public function get childDegree():uint { return _childEdges==null ? 0 : _childEdges.length; }
/** The number of inlinks and outlinks. */
public function get degree():uint { return inDegree + outDegree; }
/** The number of inlinks. */
public function get inDegree():uint { return _inEdges==null ? 0 : _inEdges.length; }
/** The number of outlinks. */
public function get outDegree():uint { return _outEdges==null ? 0 : _outEdges.length; }
/** The depth of this node in the tree structure. A value of zero
* indicates that this is a root node or that there is no tree. */
public function get depth():uint {
for (var d:uint=0, p:NodeSprite=parentNode; p!=null; p=p.parentNode, d++);
return d;
}
// -- Node Access Properties ---------------------------
/** The parent of this node in the tree structure. */
public function get parentNode():NodeSprite
{
return _parentEdge == null ? null : _parentEdge.other(this);
}
/** The first child of this node in the tree structure. */
public function get firstChildNode():NodeSprite
{
return childDegree > 0 ? _childEdges[0].other(this) : null;
}
/** The last child of this node in the tree structure. */
public function get lastChildNode():NodeSprite
{
var len:uint = childDegree;
return len > 0 ? _childEdges[len-1].other(this) : null;
}
/** The next sibling of this node in the tree structure. */
public function get nextNode():NodeSprite
{
var p:NodeSprite = parentNode, i:int = _idx+1;
if (p == null || i >= p.childDegree) return null;
return parentNode.getChildNode(i);
}
/** The previous sibling of this node in the tree structure. */
public function get prevNode():NodeSprite
{
var p:NodeSprite = parentNode, i:int = _idx-1;
if (p == null || i < 0) return null;
return parentNode.getChildNode(i);
}
// -- Position Overrides -------------------------------
/** @inheritDoc */
public override function set x(v:Number):void
{
if (x!=v) dirtyEdges();
super.x = v;
}
/** @inheritDoc */
public override function set y(v:Number):void
{
if (y!=v) dirtyEdges();
super.y = v;
}
/** @inheritDoc */
public override function set radius(r:Number):void
{
if (_radius!=r) dirtyEdges();
super.radius = r;
}
/** @inheritDoc */
public override function set angle(a:Number):void
{
if (_angle!=a) dirtyEdges();
super.angle = a;
}
// -- Methods ---------------------------------------------------------
/** Mark all incident edges as dirty. */
private function dirtyEdges():void
{
var e:EdgeSprite;
if (_parentEdge) _parentEdge.dirty();
if (_childEdges) for each (e in _childEdges) { e.dirty(); }
if (_outEdges) for each (e in _outEdges) { e.dirty(); }
if (_inEdges) for each (e in _inEdges) { e.dirty(); }
}
// -- Test Methods -------------------------------------
/**
* Indicates if the input node is connected to this node by an edge.
* @param n the node to check for connection
* @param opt flag indicating which links to check
* @return true if connected, false otherwise
*/
public function isConnected(n:NodeSprite, opt:uint=ALL_LINKS):Boolean
{
return visitNodes(
function(d:NodeSprite):Boolean { return n==d; },
opt);
}
// -- Accessor Methods ---------------------------------
/**
* Gets the child edge at the specified position
* @param i the position of the child edge
* @return the child edge
*/
public function getChildEdge(i:uint):EdgeSprite
{
return _childEdges[i];
}
/**
* Gets the child node at the specified position
* @param i the position of the child node
* @return the child node
*/
public function getChildNode(i:uint):NodeSprite
{
return _childEdges[i].other(this);
}
/**
* Gets the inlink edge at the specified position
* @param i the position of the inlink edge
* @return the inlink edge
*/
public function getInEdge(i:uint):EdgeSprite
{
return _inEdges[i];
}
/**
* Gets the inlink node at the specified position
* @param i the position of the inlink node
* @return the inlink node
*/
public function getInNode(i:uint):NodeSprite
{
return _inEdges[i].source;
}
/**
* Gets the outlink edge at the specified position
* @param i the position of the outlink edge
* @return the outlink edge
*/
public function getOutEdge(i:uint):EdgeSprite
{
return _outEdges[i];
}
/**
* Gets the outlink node at the specified position
* @param i the position of the outlink node
* @return the outlink node
*/
public function getOutNode(i:uint):NodeSprite
{
return _outEdges[i].target;
}
// -- Mutator Methods ----------------------------------
/**
* Adds a child edge to this node.
* @param e the edge to add to the child links list
* @return the index of the added edge in the list
*/
public function addChildEdge(e:EdgeSprite):uint
{
if (_childEdges == null) _childEdges = new Array();
_childEdges.push(e);
return _childEdges.length - 1;
}
/**
* Adds an inlink edge to this node.
* @param e the edge to add to the inlinks list
* @return the index of the added edge in the list
*/
public function addInEdge(e:EdgeSprite):uint
{
if (_inEdges == null) _inEdges = new Array();
_inEdges.push(e);
return _inEdges.length - 1;
}
/**
* Adds an outlink edge to this node.
* @param e the edge to add to the outlinks list
* @return the index of the added edge in the list
*/
public function addOutEdge(e:EdgeSprite):uint
{
if (_outEdges == null) _outEdges = new Array();
_outEdges.push(e);
return _outEdges.length - 1;
}
/**
* Removes all edges incident on this node. Note that this method
* does not update the edges themselves or the other nodes.
*/
public function removeAllEdges():void
{
removeEdges(ALL_LINKS);
}
/**
* Removes all edges of the indicated edge type. Note that this method
* does not update the edges themselves or the other nodes.
* @param type the type of edges to remove. For example, IN_LINKS,
* OUT_LINKS, TREE_LINKS, etc.
*/
public function removeEdges(type:int):void
{
var e:EdgeSprite;
if (type & PARENT_LINK && _parentEdge) {
_parentEdge = null;
}
if (type & CHILD_LINKS && _childEdges) {
while (_childEdges.length > 0) { e=_childEdges.pop(); }
}
if (type & OUT_LINKS && _outEdges) {
while (_outEdges.length > 0) { e=_outEdges.pop(); }
}
if (type & IN_LINKS && _inEdges) {
while (_inEdges.length > 0) { e=_inEdges.pop(); }
}
}
/**
* Removes an edge from the child links list. Note that this method
* does not update the edge itself or the other node.
* @param e the edge to remove
*/
public function removeChildEdge(e:EdgeSprite):void
{
Arrays.remove(_childEdges, e);
}
/**
* Removes an edge from the inlinks list. Note that this method
* does not update the edge itself or the other node.
* @param e the edge to remove
*/
public function removeInEdge(e:EdgeSprite):void
{
Arrays.remove(_inEdges, e);
}
/**
* Removes an edge from the outlinks list. Note that this method
* does not update the edge itself or the other node.
* @param e the edge to remove
*/
public function removeOutEdge(e:EdgeSprite):void
{
Arrays.remove(_outEdges, e);
}
// -- Visitor Methods --------------------------------------------------
/**
* Sorts the order of connected edges according to their properties.
* Each type of edge (in, out, or child) is sorted separately.
* @param opt flag indicating which set(s) of edges should be sorted
* @param sort the sort arguments.
* If a String is provided, the data will be sorted in ascending order
* according to the data field named by the string.
* If an Array is provided, the data will be sorted according to the
* fields in the array. In addition, field names can optionally
* be followed by a boolean value. If true, the data is sorted in
* ascending order (the default). If false, the data is sorted in
* descending order.
*/
public function sortEdgesBy(opt:uint=ALL_LINKS, ...sort):void
{
if (sort.length == 0) return;
if (sort[0] is Array) sort = sort[0];
var s:Function = Sort.$(sort);
if (opt & IN_LINKS && _inEdges != null) _inEdges.sort(s);
if (opt & OUT_LINKS && _outEdges != null) _outEdges.sort(s);
if (opt & CHILD_LINKS && _childEdges != null) {
_childEdges.sort(s);
for (var i:uint=0; i<_childEdges.length; ++i)
_childEdges[i].other(this).parentIndex = i;
}
}
/**
* Visits this node's edges, invoking a function on each visited edge.
* @param f the function to invoke on the edges. If the function
* returns true, the visitation is ended with an early exit.
* @param opt flag indicating which sets of edges should be visited
* @return true if the visitation was interrupted with an early exit
*/
public function visitEdges(f:Function, opt:uint=ALL_LINKS,
filter:*=null):Boolean
{
var ff:Function = Filter.$(filter);
var rev:Boolean = (opt & REVERSE) > 0;
if (opt & IN_LINKS && _inEdges != null) {
if (visitEdgeHelper(f, _inEdges, rev, ff)) return true;
}
if (opt & OUT_LINKS && _outEdges != null) {
if (visitEdgeHelper(f, _outEdges, rev, ff)) return true;
}
if (opt & CHILD_LINKS && _childEdges != null) {
if (visitEdgeHelper(f, _childEdges, rev, ff)) return true;
}
if (opt & PARENT_LINK && _parentEdge != null) {
if ((ff==null || ff(_parentEdge)) && f(_parentEdge))
return true;
}
return false;
}
private function visitEdgeHelper(f:Function, a:Array, r:Boolean,
ff:Function):Boolean
{
var i:uint, n:uint=a.length, v:*;
if (r) {
for (i=n; --i>=0;) {
if ((ff==null || ff(a[i])) && f(a[i]) as Boolean)
return true;
}
} else {
for (i=0; i
if ((ff==null || ff(a[i])) && f(a[i]) as Boolean)
return true;
}
}
return false;
}
/**
* Visits the nodes connected to this node by edges, invoking a
* function on each visited node.
* @param f the function to invoke on the nodes. If the function
* returns true, the visitation is ended with an early exit.
* @param opt flag indicating which sets of edges should be traversed
* @return true if the visitation was interrupted with an early exit
*/
public function visitNodes(f:Function, opt:uint=ALL_LINKS,
filter:*=null):Boolean
{
var ff:Function = Filter.$(filter);
var rev:Boolean = (opt & REVERSE) > 0;
if (opt & IN_LINKS && _inEdges != null) {
if (visitNodeHelper(f, _inEdges, rev, ff)) return true;
}
if (opt & OUT_LINKS && _outEdges != null) {
if (visitNodeHelper(f, _outEdges, rev, ff)) return true;
}
if (opt & CHILD_LINKS && _childEdges != null) {
if (visitNodeHelper(f, _childEdges, rev, ff)) return true;
}
if (opt & PARENT_LINK && _parentEdge != null) {
if ((ff==null||ff(_parentEdge)) && f(_parentEdge.other(this)))
return true;
}
return false;
}
private function visitNodeHelper(f:Function, a:Array, r:Boolean,
ff:Function):Boolean
{
var i:uint, n:uint=a.length, u:NodeSprite;
if (r) {
for (i=n; --i>=0;) {
u = a[i].other(this);
if ((ff==null || ff(u)) && f(u) as Boolean)
return true;
}
} else {
for (i=0; i
u = a[i].other(this);
if ((ff==null || ff(u)) && f(u) as Boolean)
return true;
}
}
return false;
}
/**
* Visits the subtree rooted at this node using a depth first search,
* invoking the input function on each visited node.
* @param f the function to invoke on the nodes. If the function
* returns true, the visitation is ended with an early exit.
* @param preorder if true, nodes are visited in a pre-order traversal;
* if false, they are visited in a post-order traversal
* @return true if the visitation was interrupted with an early exit
*/
public function visitTreeDepthFirst(f:Function, preorder:Boolean=false):Boolean
{
if (preorder && (f(this) as Boolean)) return true;
for (var i:uint = 0; i
if (getChildNode(i).visitTreeDepthFirst(f, preorder))
return true;
}
if (!preorder && (f(this) as Boolean)) return true;
return false;
}
/**
* Visits the subtree rooted at this node using a breadth first search,
* invoking the input function on each visited node.
* @param f the function to invoke on the nodes. If the function
* returns true, the visitation is ended with an early exit.
* @return true if the visitation was interrupted with an early exit
*/
public function visitTreeBreadthFirst(f:Function):Boolean
{
var q:Array = new Array(), x:NodeSprite;
q.push(this);
while (q.length > 0) {
if (f(x=q.shift()) as Boolean) return true;
for (var i:uint = 0; i
q.push(x.getChildNode(i));
}
return false;
}
/**
* Sets property values on edge sprites connected to this node.
* @param vals an object containing the properties and values to set.
* @param opt flag indicating which sets of edges should be traversed
* @param trans a transitioner or time span for updating object values.
* If the input is a transitioner, it will be used to store the
* updated values. If the input is a number, a new Transitioner with
* duration set to the input value will be used. The input is null by
* default, in which case object values are updated immediately.
* @param filter an optional Boolean-valued filter function for
* limiting which items are visited
* @return the transitioner used to update the values
*/
public function setEdgeProperties(vals:Object, opt:uint=ALL_LINKS,
trans:*=null, filter:*=null):Transitioner
{
var t:Transitioner = Transitioner.instance(trans);
for (var name:String in vals) {
var val:* = vals[name];
var v:Function = val is Function ? val as Function
: val is IEvaluable ? IEvaluable(val).eval : null;
visitEdges(function(s:EdgeSprite):void {
t.setValue(s, name, (v!=null ? v(t.$(s)) : val));
}, opt, filter);
}
return t;
}
/**
* Sets property values on node sprites connected to this node.
* @param vals an object containing the properties and values to set.
* @param opt flag indicating which sets of nodes should be traversed
* @param trans a transitioner or time span for updating object values.
* If the input is a transitioner, it will be used to store the
* updated values. If the input is a number, a new Transitioner with
* duration set to the input value will be used. The input is null by
* default, in which case object values are updated immediately.
* @param filter an optional Boolean-valued filter function for
* limiting which items are visited
* @return the transitioner used to update the values
*/
public function setNodeProperties(vals:Object, opt:uint=ALL_LINKS,
trans:*=null, filter:*=null):Transitioner
{
var t:Transitioner = Transitioner.instance(trans);
for (var name:String in vals) {
var val:* = vals[name];
var v:Function = val is Function ? val as Function
: val is IEvaluable ? IEvaluable(val).eval : null;
visitNodes(function(n:NodeSprite):void {
t.setValue(n, name, (v!=null ? v(t.$(n)) : val));
}, opt, filter);
}
return t;
}
} // end of class NodeSprite
}
{
import flare.animate.Transitioner;
import flare.util.Arrays;
import flare.util.Filter;
import flare.util.IEvaluable;
import flare.util.Sort;
/**
* Visually represents a data element, such as a data tuple or graph node.
* By default, NodeSprites are drawn using a
.
* NodeSprites are typically managed by a Data
object.
*
*
NodeSprites can separately maintain adjacency lists for both a
* general graph structure (managing lists for inlinks and outlinks) and a
* tree structure (managing a list for child links and a parent pointer).
* The graph and tree lists are maintained completely separately to
* maximize flexibility. While the the tree lists are often used to record
* a spanning tree of the general network structure, they can also be used
* to represent a hierarchy completely distinct from a co-existing graph
* structure. Take this into account when iterating over the edges incident
* on this node.
*/
public class NodeSprite extends DataSprite
{
/** Flag indicating inlinks, edges that point to this node. */
public static const IN_LINKS:uint = 1;
/** Flag indicating outlinks, edges that point away from node. */
public static const OUT_LINKS:uint = 2;
/** Flag indicating both inlinks and outlinks. */
public static const GRAPH_LINKS:uint = 3; // IN_LINKS | OUT_LINKS
/** Flag indicating child links in a tree structure. */
public static const CHILD_LINKS:uint = 4;
/** Flag indicating the link to a parent in a tree structure. */
public static const PARENT_LINK:uint = 8;
/** Flag indicating both child and parent links. */
public static const TREE_LINKS:uint = 12; // CHILD_LINKS | PARENT_LINK
/** Flag indicating all links, including graph and tree links. */
public static const ALL_LINKS:uint = 15; // GRAPH_LINKS | TREE_LINKS
/** Flag indicating that a traversal should be performed in reverse. */
public static const REVERSE:uint = 16;
// -- Properties ------------------------------------------------------
private var _parentEdge:EdgeSprite;
private var _idx:int = -1; // node index in parent's array
private var _childEdges:/*EdgeSprite*/Array;
private var _inEdges:/*EdgeSprite*/Array;
private var _outEdges:/*EdgeSprite*/Array;
private var _expanded:Boolean = true;
/** Flag indicating if this node is currently expanded. This flag can
* be used by layout routines to expand/collapse connections. */
public function get expanded():Boolean { return _expanded; }
public function set expanded(b:Boolean):void { _expanded = b; }
/** The edge connecting this node to its parent in a tree structure. */
public function get parentEdge():EdgeSprite { return _parentEdge; }
public function set parentEdge(e:EdgeSprite):void { _parentEdge = e; }
/** The index of this node in its tree parent's child links list. */
public function get parentIndex():int { return _idx; }
public function set parentIndex(i:int):void { _idx = i; }
// -- Node Degree Properties ------------------------------------------
/** The number of child links. */
public function get childDegree():uint { return _childEdges==null ? 0 : _childEdges.length; }
/** The number of inlinks and outlinks. */
public function get degree():uint { return inDegree + outDegree; }
/** The number of inlinks. */
public function get inDegree():uint { return _inEdges==null ? 0 : _inEdges.length; }
/** The number of outlinks. */
public function get outDegree():uint { return _outEdges==null ? 0 : _outEdges.length; }
/** The depth of this node in the tree structure. A value of zero
* indicates that this is a root node or that there is no tree. */
public function get depth():uint {
for (var d:uint=0, p:NodeSprite=parentNode; p!=null; p=p.parentNode, d++);
return d;
}
// -- Node Access Properties ---------------------------
/** The parent of this node in the tree structure. */
public function get parentNode():NodeSprite
{
return _parentEdge == null ? null : _parentEdge.other(this);
}
/** The first child of this node in the tree structure. */
public function get firstChildNode():NodeSprite
{
return childDegree > 0 ? _childEdges[0].other(this) : null;
}
/** The last child of this node in the tree structure. */
public function get lastChildNode():NodeSprite
{
var len:uint = childDegree;
return len > 0 ? _childEdges[len-1].other(this) : null;
}
/** The next sibling of this node in the tree structure. */
public function get nextNode():NodeSprite
{
var p:NodeSprite = parentNode, i:int = _idx+1;
if (p == null || i >= p.childDegree) return null;
return parentNode.getChildNode(i);
}
/** The previous sibling of this node in the tree structure. */
public function get prevNode():NodeSprite
{
var p:NodeSprite = parentNode, i:int = _idx-1;
if (p == null || i < 0) return null;
return parentNode.getChildNode(i);
}
// -- Position Overrides -------------------------------
/** @inheritDoc */
public override function set x(v:Number):void
{
if (x!=v) dirtyEdges();
super.x = v;
}
/** @inheritDoc */
public override function set y(v:Number):void
{
if (y!=v) dirtyEdges();
super.y = v;
}
/** @inheritDoc */
public override function set radius(r:Number):void
{
if (_radius!=r) dirtyEdges();
super.radius = r;
}
/** @inheritDoc */
public override function set angle(a:Number):void
{
if (_angle!=a) dirtyEdges();
super.angle = a;
}
// -- Methods ---------------------------------------------------------
/** Mark all incident edges as dirty. */
private function dirtyEdges():void
{
var e:EdgeSprite;
if (_parentEdge) _parentEdge.dirty();
if (_childEdges) for each (e in _childEdges) { e.dirty(); }
if (_outEdges) for each (e in _outEdges) { e.dirty(); }
if (_inEdges) for each (e in _inEdges) { e.dirty(); }
}
// -- Test Methods -------------------------------------
/**
* Indicates if the input node is connected to this node by an edge.
* @param n the node to check for connection
* @param opt flag indicating which links to check
* @return true if connected, false otherwise
*/
public function isConnected(n:NodeSprite, opt:uint=ALL_LINKS):Boolean
{
return visitNodes(
function(d:NodeSprite):Boolean { return n==d; },
opt);
}
// -- Accessor Methods ---------------------------------
/**
* Gets the child edge at the specified position
* @param i the position of the child edge
* @return the child edge
*/
public function getChildEdge(i:uint):EdgeSprite
{
return _childEdges[i];
}
/**
* Gets the child node at the specified position
* @param i the position of the child node
* @return the child node
*/
public function getChildNode(i:uint):NodeSprite
{
return _childEdges[i].other(this);
}
/**
* Gets the inlink edge at the specified position
* @param i the position of the inlink edge
* @return the inlink edge
*/
public function getInEdge(i:uint):EdgeSprite
{
return _inEdges[i];
}
/**
* Gets the inlink node at the specified position
* @param i the position of the inlink node
* @return the inlink node
*/
public function getInNode(i:uint):NodeSprite
{
return _inEdges[i].source;
}
/**
* Gets the outlink edge at the specified position
* @param i the position of the outlink edge
* @return the outlink edge
*/
public function getOutEdge(i:uint):EdgeSprite
{
return _outEdges[i];
}
/**
* Gets the outlink node at the specified position
* @param i the position of the outlink node
* @return the outlink node
*/
public function getOutNode(i:uint):NodeSprite
{
return _outEdges[i].target;
}
// -- Mutator Methods ----------------------------------
/**
* Adds a child edge to this node.
* @param e the edge to add to the child links list
* @return the index of the added edge in the list
*/
public function addChildEdge(e:EdgeSprite):uint
{
if (_childEdges == null) _childEdges = new Array();
_childEdges.push(e);
return _childEdges.length - 1;
}
/**
* Adds an inlink edge to this node.
* @param e the edge to add to the inlinks list
* @return the index of the added edge in the list
*/
public function addInEdge(e:EdgeSprite):uint
{
if (_inEdges == null) _inEdges = new Array();
_inEdges.push(e);
return _inEdges.length - 1;
}
/**
* Adds an outlink edge to this node.
* @param e the edge to add to the outlinks list
* @return the index of the added edge in the list
*/
public function addOutEdge(e:EdgeSprite):uint
{
if (_outEdges == null) _outEdges = new Array();
_outEdges.push(e);
return _outEdges.length - 1;
}
/**
* Removes all edges incident on this node. Note that this method
* does not update the edges themselves or the other nodes.
*/
public function removeAllEdges():void
{
removeEdges(ALL_LINKS);
}
/**
* Removes all edges of the indicated edge type. Note that this method
* does not update the edges themselves or the other nodes.
* @param type the type of edges to remove. For example, IN_LINKS,
* OUT_LINKS, TREE_LINKS, etc.
*/
public function removeEdges(type:int):void
{
var e:EdgeSprite;
if (type & PARENT_LINK && _parentEdge) {
_parentEdge = null;
}
if (type & CHILD_LINKS && _childEdges) {
while (_childEdges.length > 0) { e=_childEdges.pop(); }
}
if (type & OUT_LINKS && _outEdges) {
while (_outEdges.length > 0) { e=_outEdges.pop(); }
}
if (type & IN_LINKS && _inEdges) {
while (_inEdges.length > 0) { e=_inEdges.pop(); }
}
}
/**
* Removes an edge from the child links list. Note that this method
* does not update the edge itself or the other node.
* @param e the edge to remove
*/
public function removeChildEdge(e:EdgeSprite):void
{
Arrays.remove(_childEdges, e);
}
/**
* Removes an edge from the inlinks list. Note that this method
* does not update the edge itself or the other node.
* @param e the edge to remove
*/
public function removeInEdge(e:EdgeSprite):void
{
Arrays.remove(_inEdges, e);
}
/**
* Removes an edge from the outlinks list. Note that this method
* does not update the edge itself or the other node.
* @param e the edge to remove
*/
public function removeOutEdge(e:EdgeSprite):void
{
Arrays.remove(_outEdges, e);
}
// -- Visitor Methods --------------------------------------------------
/**
* Sorts the order of connected edges according to their properties.
* Each type of edge (in, out, or child) is sorted separately.
* @param opt flag indicating which set(s) of edges should be sorted
* @param sort the sort arguments.
* If a String is provided, the data will be sorted in ascending order
* according to the data field named by the string.
* If an Array is provided, the data will be sorted according to the
* fields in the array. In addition, field names can optionally
* be followed by a boolean value. If true, the data is sorted in
* ascending order (the default). If false, the data is sorted in
* descending order.
*/
public function sortEdgesBy(opt:uint=ALL_LINKS, ...sort):void
{
if (sort.length == 0) return;
if (sort[0] is Array) sort = sort[0];
var s:Function = Sort.$(sort);
if (opt & IN_LINKS && _inEdges != null) _inEdges.sort(s);
if (opt & OUT_LINKS && _outEdges != null) _outEdges.sort(s);
if (opt & CHILD_LINKS && _childEdges != null) {
_childEdges.sort(s);
for (var i:uint=0; i<_childEdges.length; ++i)
_childEdges[i].other(this).parentIndex = i;
}
}
/**
* Visits this node's edges, invoking a function on each visited edge.
* @param f the function to invoke on the edges. If the function
* returns true, the visitation is ended with an early exit.
* @param opt flag indicating which sets of edges should be visited
* @return true if the visitation was interrupted with an early exit
*/
public function visitEdges(f:Function, opt:uint=ALL_LINKS,
filter:*=null):Boolean
{
var ff:Function = Filter.$(filter);
var rev:Boolean = (opt & REVERSE) > 0;
if (opt & IN_LINKS && _inEdges != null) {
if (visitEdgeHelper(f, _inEdges, rev, ff)) return true;
}
if (opt & OUT_LINKS && _outEdges != null) {
if (visitEdgeHelper(f, _outEdges, rev, ff)) return true;
}
if (opt & CHILD_LINKS && _childEdges != null) {
if (visitEdgeHelper(f, _childEdges, rev, ff)) return true;
}
if (opt & PARENT_LINK && _parentEdge != null) {
if ((ff==null || ff(_parentEdge)) && f(_parentEdge))
return true;
}
return false;
}
private function visitEdgeHelper(f:Function, a:Array, r:Boolean,
ff:Function):Boolean
{
var i:uint, n:uint=a.length, v:*;
if (r) {
for (i=n; --i>=0;) {
if ((ff==null || ff(a[i])) && f(a[i]) as Boolean)
return true;
}
} else {
for (i=0; i
if ((ff==null || ff(a[i])) && f(a[i]) as Boolean)
return true;
}
}
return false;
}
/**
* Visits the nodes connected to this node by edges, invoking a
* function on each visited node.
* @param f the function to invoke on the nodes. If the function
* returns true, the visitation is ended with an early exit.
* @param opt flag indicating which sets of edges should be traversed
* @return true if the visitation was interrupted with an early exit
*/
public function visitNodes(f:Function, opt:uint=ALL_LINKS,
filter:*=null):Boolean
{
var ff:Function = Filter.$(filter);
var rev:Boolean = (opt & REVERSE) > 0;
if (opt & IN_LINKS && _inEdges != null) {
if (visitNodeHelper(f, _inEdges, rev, ff)) return true;
}
if (opt & OUT_LINKS && _outEdges != null) {
if (visitNodeHelper(f, _outEdges, rev, ff)) return true;
}
if (opt & CHILD_LINKS && _childEdges != null) {
if (visitNodeHelper(f, _childEdges, rev, ff)) return true;
}
if (opt & PARENT_LINK && _parentEdge != null) {
if ((ff==null||ff(_parentEdge)) && f(_parentEdge.other(this)))
return true;
}
return false;
}
private function visitNodeHelper(f:Function, a:Array, r:Boolean,
ff:Function):Boolean
{
var i:uint, n:uint=a.length, u:NodeSprite;
if (r) {
for (i=n; --i>=0;) {
u = a[i].other(this);
if ((ff==null || ff(u)) && f(u) as Boolean)
return true;
}
} else {
for (i=0; i
u = a[i].other(this);
if ((ff==null || ff(u)) && f(u) as Boolean)
return true;
}
}
return false;
}
/**
* Visits the subtree rooted at this node using a depth first search,
* invoking the input function on each visited node.
* @param f the function to invoke on the nodes. If the function
* returns true, the visitation is ended with an early exit.
* @param preorder if true, nodes are visited in a pre-order traversal;
* if false, they are visited in a post-order traversal
* @return true if the visitation was interrupted with an early exit
*/
public function visitTreeDepthFirst(f:Function, preorder:Boolean=false):Boolean
{
if (preorder && (f(this) as Boolean)) return true;
for (var i:uint = 0; i
if (getChildNode(i).visitTreeDepthFirst(f, preorder))
return true;
}
if (!preorder && (f(this) as Boolean)) return true;
return false;
}
/**
* Visits the subtree rooted at this node using a breadth first search,
* invoking the input function on each visited node.
* @param f the function to invoke on the nodes. If the function
* returns true, the visitation is ended with an early exit.
* @return true if the visitation was interrupted with an early exit
*/
public function visitTreeBreadthFirst(f:Function):Boolean
{
var q:Array = new Array(), x:NodeSprite;
q.push(this);
while (q.length > 0) {
if (f(x=q.shift()) as Boolean) return true;
for (var i:uint = 0; i
q.push(x.getChildNode(i));
}
return false;
}
/**
* Sets property values on edge sprites connected to this node.
* @param vals an object containing the properties and values to set.
* @param opt flag indicating which sets of edges should be traversed
* @param trans a transitioner or time span for updating object values.
* If the input is a transitioner, it will be used to store the
* updated values. If the input is a number, a new Transitioner with
* duration set to the input value will be used. The input is null by
* default, in which case object values are updated immediately.
* @param filter an optional Boolean-valued filter function for
* limiting which items are visited
* @return the transitioner used to update the values
*/
public function setEdgeProperties(vals:Object, opt:uint=ALL_LINKS,
trans:*=null, filter:*=null):Transitioner
{
var t:Transitioner = Transitioner.instance(trans);
for (var name:String in vals) {
var val:* = vals[name];
var v:Function = val is Function ? val as Function
: val is IEvaluable ? IEvaluable(val).eval : null;
visitEdges(function(s:EdgeSprite):void {
t.setValue(s, name, (v!=null ? v(t.$(s)) : val));
}, opt, filter);
}
return t;
}
/**
* Sets property values on node sprites connected to this node.
* @param vals an object containing the properties and values to set.
* @param opt flag indicating which sets of nodes should be traversed
* @param trans a transitioner or time span for updating object values.
* If the input is a transitioner, it will be used to store the
* updated values. If the input is a number, a new Transitioner with
* duration set to the input value will be used. The input is null by
* default, in which case object values are updated immediately.
* @param filter an optional Boolean-valued filter function for
* limiting which items are visited
* @return the transitioner used to update the values
*/
public function setNodeProperties(vals:Object, opt:uint=ALL_LINKS,
trans:*=null, filter:*=null):Transitioner
{
var t:Transitioner = Transitioner.instance(trans);
for (var name:String in vals) {
var val:* = vals[name];
var v:Function = val is Function ? val as Function
: val is IEvaluable ? IEvaluable(val).eval : null;
visitNodes(function(n:NodeSprite):void {
t.setValue(n, name, (v!=null ? v(t.$(n)) : val));
}, opt, filter);
}
return t;
}
} // end of class NodeSprite
}
Edge Renderer
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'sshape
* property and control points found in thepoints
property.
* The line rendering properties are set by thesetLineStyle
* 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, thescaleMode
and
*caps
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 /= dd; dy /= dd;// set final point positions
dd = e.lineWidth/2;
// if drawing as lines, offset arrow tip by half the line width
if (e.arrowType == ArrowType.LINES)
{
_p.x -= dd*dx;}
_p.y -= dd*dy;
dd += e.lineWidth;
// offset the anchor point (the end point for the edge connector)}
// so that edge doesn't "overshoot" the arrow head
dd = ah - dd;
x2 = _p.x - dd*dx;
y2 = _p.y - dd*dy;
// draw the edge
g.clear(); // clear it out
setLineStyle(e, g); // set the line style
if (e.shape == Shapes.BEZIER && ctrls != null && ctrls.length > 1)
{
if (ctrls.length < 4){g.moveTo(x1, y1);}
g.curveTo(ctrls[0], ctrls[1], x2, y2);
else
{
Shapes.drawCubic(g, x1, y1, ctrls[0], ctrls[1],}
ctrls[2], ctrls[3], x2, y2);}else if (e.shape == Shapes.CARDINAL){
Shapes.consolidate(x1, y1, ctrls, x2, y2, _pts);}
Shapes.drawCardinal(g, _pts, 2+ctrls.length/2);else if (e.shape == Shapes.BSPLINE)
{
Shapes.consolidate(x1, y1, ctrls, x2, y2, _pts);}
Shapes.drawBSpline(g, _pts, 2+ctrls.length/2);else
{
g.moveTo(x1, y1);
if (ctrls != null)
{
for (var i:uint=0; ig.lineTo(ctrls[i], ctrls[i+1]);}g.lineTo(x2, y2);}// draw an arrow
if (e.arrowType != ArrowType.NONE)
{
// get other arrow points
x1 = _p.x - ah*dx + aw*dy; y1 = _p.y - ah*dy - aw*dx;
x2 = _p.x - ah*dx - aw*dy; y2 = _p.y - ah*dy + aw*dx;
if (e.arrowType == ArrowType.TRIANGLE)
{
g.lineStyle();}
g.moveTo(_p.x, _p.y);
g.beginFill(e.lineColor, e.lineAlpha);
g.lineTo(x1, y1);
g.lineTo(x2, y2);
g.endFill();
else if (e.arrowType == ArrowType.LINES)
{
g.moveTo(x1, y1);
g.lineTo(_p.x, _p.y);
g.lineTo(x2, y2);}}}
/**
* Sets the line style for edge rendering.
* @param e the EdgeSprite to render
* @param g the Graphics context to draw with
*/
protected function setLineStyle(e:EdgeSprite, g:Graphics):void
{
var lineAlpha:Number = e.lineAlpha;}
if (lineAlpha == 0) return;
g.lineStyle(e.lineWidth, e.lineColor, lineAlpha,
pixelHinting, scaleMode, caps, joints, miterLimit);
} // end of class EdgeRenderer
Subscribe to:
Posts (Atom)