This time, using a BFS (Breadth First Search - FIFO), it highlights all connected nodes and edges from the node you select.
-x to make controls appear and disappear
-right click on node to delete it, and all the nodes and edges along paths connected
-left click and move mouse to drag node and redraw immediately connected edges.
-hit reload to get new graph
// forked from NME's Not done with this yet
// forked from NME's Nodes Star 2 - [Updated]
// forked from NME's Nodes Star 1 - randomly connected
// @author - NME a.k.a. Anthony Pace ap13mo@student.ocadu.ca - Toronto, ON, Canada
//Not done yet, in fact I just started this... doing this when I have a couple minutes here and there, because I have a ton of Homework.
package {
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.utils.Dictionary;
public class FlashTest extends Sprite {
public var hoverNodeColour:uint = 0x333388;
public var hoverEdgeColour:uint = 0x0000ff;
public var selectedEdgeColour:uint = 0xff0000;
public var unselectedEdgeColour:uint = 0x000000;
public var selectedNodeColour:uint = 0xff0080;
public var unselectedNodeColour:uint = 0x888888;
public var nodeRadius:uint = 4;
public var nodeLayer:Sprite;
public var edgeLayer:Sprite;
public function FlashTest() {
Wonderfl.disable_capture ();//
init();
}
private function init():void{
removeEventListener(Event.ADDED_TO_STAGE, init);
var offset:Pt = new Pt(stage.stageWidth/2,stage.stageHeight/2);//used as centre of "circle" and as control point for bezier curves
var maxNodes:uint=60+Math.random()*120;//play around here and later in the equation to get nifty constructs
var radius:uint = nodeRadius;
var pix2:Number = Math.PI*2;
var r2d:Number = pix2/180;
var bigRad:uint = offset.x-radius-40; //the radius (a.k.a. distance) for the nodes from the centre of the collective 'circle'
var r2dx180_nodes:Number = r2d*(180/maxNodes);
edgeLayer = new Sprite(); //Could have been a shape layer, but I want to draw edges individually
nodeLayer = new Sprite();
var nodes:Vector.<Node> = new Vector.<Node>;//I am only storing references here temporarily in this case, because there isn't a reason, at least right now, to store them this way after they've been generated
//make the nodes, and give them their coordinates
var node1:Node, node2:Node;
var radian:Number;
for(var i:uint=0;i!=maxNodes;i++){
node1 = new Node(i,radius,unselectedNodeColour);
radian = i*r2dx180_nodes;
node1.x = offset.x + Math.cos(radian)*(bigRad);
node1.y = offset.y + Math.sin(radian)*(bigRad);
nodeLayer.addChild(node1);
nodes.push(node1);
}
Edge.anchorPt = offset; //This is where I define the control point that will be static among all Edges
//if you connect this to the mouse, it yields intersting results graphically
//setup the connections between nodes, create the edges
var edge:Edge;
var j:uint,k:uint;
for(i=0;i!=maxNodes;i++){
node1 = nodes[i];
//this is where we establish the connections
k= (i+5*(7*i+5))%maxNodes;//Math.random()*maxNodes;
node2 = nodes[k];
if(node2.connections[node1.id]===undefined && node2!=node1){
edge = makeEdge(node1,node2);
edge.drawEdge(unselectedEdgeColour);
edgeLayer.addChild(edge);
}
}
for(i=0;i!=maxNodes;i++){
node1 = nodes[i];
if(!hasConnections(node1)){//
deletePathsFromNode(node1);
}
}
nodes=null;
//make things visible
stage.addChild(edgeLayer);
stage.addChild(nodeLayer);
//these events allow the dragging of elements
nodeLayer.addEventListener(MouseEvent.MOUSE_OVER,mouseOverNLEH);
nodeLayer.addEventListener(MouseEvent.MOUSE_OUT,mouseOutNLEH);
nodeLayer.addEventListener(MouseEvent.MOUSE_DOWN,mouseDownNLEH);
//when the mouse moves faster than image is rendered... listening in a parent layer
//or even globally for movement solves that problem, and it cuts down on the amount of listeners
stage.addEventListener(MouseEvent.MOUSE_UP,mouseUpSEH);
stage.addEventListener(MouseEvent.MOUSE_MOVE,mouseMoveSEH);
nodeLayer.addEventListener(MouseEvent.RIGHT_CLICK,mouseRightClickNLEH);//
stage.addEventListener(MouseEvent.RIGHT_CLICK,function(me:MouseEvent):void{});
//Keyboard event to make nodes invisible
stage.addEventListener(KeyboardEvent.KEY_DOWN,
function(ke:KeyboardEvent):void{
if(ke.keyCode == 88){
if(nodeLayer.visible){
nodeLayer.visible = false;
stage.removeChild(nodeLayer);
}else {
nodeLayer.visible = true;
stage.addChild(nodeLayer);
}
}
}
);
}
/////////////////////////////////////////////////////////////////
//These variables and functions, are used for dragging the nodes
public var isDragging:Boolean = false;
public var draggedNode:Node; //if you don't know what type of class it will be, you can use Object instead
public function mouseOverNLEH(me:MouseEvent):void{
if (!draggedNode){
var node:Node = me.target as Node;
node.drawDot(nodeRadius,hoverNodeColour);
//redrawConnectedEdges(node, hoverEdgeColour);//
redrawPathsFromNode(node,hoverEdgeColour,hoverNodeColour);
}
me.updateAfterEvent();
}
public function mouseOutNLEH(me:MouseEvent):void{//
if (!isDragging){
var node:Node = me.target as Node;
node.drawDot(nodeRadius,unselectedNodeColour);
//redrawConnectedEdges(node, unselectedEdgeColour);
redrawPathsFromNode(node,unselectedEdgeColour,unselectedNodeColour);
draggedNode=null;
}
me.updateAfterEvent();
}
public function mouseDownNLEH(me:MouseEvent):void{
isDragging=true;
draggedNode = me.target as Node;
//this is where would redraw them with the move colour
draggedNode.drawDot(nodeRadius,selectedNodeColour);
redrawConnectedEdges(draggedNode, selectedEdgeColour);//
me.updateAfterEvent();
}
public function mouseUpSEH(me:MouseEvent):void{
if(isDragging){
isDragging=false;
//this is where we would set them back to normal
draggedNode.drawDot(nodeRadius,hoverNodeColour);
redrawConnectedEdges(draggedNode, hoverEdgeColour);
draggedNode=null;
}
me.updateAfterEvent();
}
public function mouseMoveSEH(me:MouseEvent):void{
if(me.buttonDown && isDragging){
draggedNode.x = me.stageX;
draggedNode.y = me.stageY;
redrawConnectedEdges(draggedNode, selectedEdgeColour);
}
me.updateAfterEvent();
}
//right click on a node, to start deleting nodes and their connected edges
public function mouseRightClickNLEH(me:MouseEvent):void{
deletePathsFromNode( me.target as Node);
me.updateAfterEvent();
}
//this function is only called when dragging node around
public function redrawConnectedEdges(node:Node,edgeColour:uint):void{//
for each(var edge:Edge in node.edges){
edge.drawEdge(edgeColour);
}
}
public function redrawPathsFromNode(startNode:Node,edgeColour:uint,nodeColour:uint):void{
//essentially a BFS ...
var visitedNodes:Dictionary = new Dictionary(); //where we've been
var drawnEdges:Dictionary = new Dictionary();
var next:Vector.<Node> = new Vector.<Node>; //where we haven't crawled through yet
var currentNode:Node; //current
var node2:Node;
var edge:Edge;
next.push(startNode);
while(next.length!=0){
currentNode = next.shift();
visitedNodes[currentNode]=true;
for each(edge in currentNode.edges){
if(edge.node1 === currentNode){
node2 = edge.node2;
}else{
node2 = edge.node1;
}
if(!(node2 in visitedNodes)){
next.push(node2);
node2.drawDot(nodeRadius,nodeColour);
}
if(!(edge in drawnEdges)){
drawnEdges[edge]=true;
edge.drawEdge(edgeColour);
}
}
}
}
//this is called after the generation of the nodes, and connections are being made
public function makeEdge(node1:Node,node2:Node):Edge{
node1.connections[node2.id] = node2;
node2.connections[node1.id] = node1;
var edge:Edge = new Edge(node1,node2);
node1.edges[edge]=edge;
node2.edges[edge]=edge;
return edge;
}
///////////////////////////////////////
//functions required for deleting nodes
public function hasConnections(node:Node):Boolean{
var oc:uint = 0;
for each(var n:Node in node.connections){
oc++;
break;
}
return Boolean(oc);
}
public function deletePathsFromNode(startNode:Node):void{
//essentially a BFS ... because I am using FIFO
var visited:Dictionary = new Dictionary(); //where we've been
var next:Vector.<Node> = new Vector.<Node>; //where we haven't crawled through yet
var currentNode:Node; //current
var node2:Node;
var edge:Edge;
next.push(startNode);
while(next.length!=0){
currentNode = next.shift();
visited[currentNode]=true;
for each(edge in currentNode.edges){
if(edge.node1 === currentNode){
node2 = edge.node2;
}else{
node2 = edge.node1;
}
if(!(node2 in visited)){
next.push(node2);
delete node2.edges[edge]; //edge reference to current edge from connected node
delete node2.connections[currentNode.id]; //connection reference to me from connected node
delete currentNode.connections[node2.id];//my connection reference to the connected node
delete currentNode.edges[edge]; //primary node's reference to the current edge
}
edgeLayer.removeChild(edge);
}
if(nodeLayer.contains(currentNode)){
nodeLayer.removeChild(currentNode); //
}
}
}
}
}
import flash.display.Graphics;
import flash.display.Sprite;
import flash.utils.Dictionary;
internal class Pt {
public var x:uint;
public var y:uint;
public function Pt(x:uint,y:uint){
this.x = x;
this.y = y;
}
}
internal class Node extends Sprite{
public var id:uint;
public var connections:Dictionary;//tells you the connections by node id
public var edges:Dictionary; //:Vector.<Edge>;
public function Node(id:uint,radius:uint=1,colour:uint=0){
this.connections = new Dictionary();
this.edges = new Dictionary();//Vector.<Edge>;
this.id=id;
drawDot(radius,colour);
}
public function drawDot(radius:uint=1,colour:uint=0):void{
//I could memoize the results, but I'm doing it this way for... I might do it later when I start playing around later
this.graphics.clear();
this.graphics.beginFill(colour);
this.graphics.drawCircle(0,0,radius);
this.cacheAsBitmap = true;
}
}
internal class Edge extends Sprite{
public var node1:Node;
public var node2:Node;
public static var anchorPt:Pt;
public function Edge (node1:Node,node2:Node){
this.node1 = node1;
this.node2 = node2;
}
public function drawEdge(colour:uint = 0x000000):void{
var g:Graphics = this.graphics;
g.clear();
g.lineStyle(.25,colour);
g.moveTo(node1.x,node1.y);
g.curveTo(anchorPt.x,anchorPt.y,node2.x,node2.y);
}
}