Author: tomithy Date: 2010-07-10 10:17:52 -0700 (Sat, 10 Jul 2010) New Revision: 20896
Modified:
cytoscapeweb/branches/gsoc2010/demos/src/GBEB/Mesh.as
Log:
/* Version control file 11/7/10
Version 0.3
- Mesh is able to merge shape;
- DataDisplay shows different Shapes in Mesh;
- Able to calculate Mesh for different Layouts.
Version 0.4
- cleanup function is written to free up resources
Mesh: - shapes no longer contain repeated edges
- Patch is used to reflect the gradient around the
point, as there is some unknown bug causing the gradient to be reflected about
the y-axis
- Functions implemented:
4a) generateMeshEdges;
getNodesForMeshEdge; isSourceNodeAssigned, generateNonRedundantShapeIndexArray,
findCentroid,
generateLineFromPointAndGradient, intersectionWithVertical,
intersectionWithHorizontal
4b) mergeNodes_All(),
mergeNodes_Pairwise, calculateDistanceBetweenNodes - disabled as there are some
bugs
Class GeometryUtil is added to store lineIntersectline Math function
DataDisplay: - displays the centroid of Shapes,
- displays the mesh itself, which is
accurate except for occurance of vertical straight lines due to known issue (1)
Version 0.5
Shape/ DataDisplay: - control points are set up. Initially they are added via
GBEB properties and mirrored in edge.points
- control points are successfully added to all dataEdges that intersects with
meshEdges - the control point is the avg of the intersection points of
the meshEdge with the dataEdges passing through it.
Version 0.6
Bundler added - it extends from edge render. Used Shapes.BEZIER for rendering,
the effect is only satisfactory in general, but sometimes confusing for lines
with > 1 control points,
due to BEZIER's mathemathical property
Comments: It doesnt work very well on simple graphs and the curves thens to
complicate.
Messy graphs like circle or radial can
be improved if the resolution is set higher at the expense of runtime.
Next:
Version 0.7 - allow dynamic adjusting of resolution by merging Shapes by the
factor of 2
- include edge quality
calculation
- implement another renderer
for meshEdges to obtain better graphical control that default Bezier does not
offer.
Comments: Indent layout is rather slow to compute. Perhaps because of the huge
centre shape.
Known issues: 1) If the mesh edge is vertical or horizontal and it lies
directly above the grid, its source/traget nodes cannot be retrieve.
Eg. Edge 9 of
"social network.xml" -
Function
responsible: intersectsVertical and getNodesForMeshEdge
2) MergeNodes_All() -
Is acting weirdly, so it is disabled for now.
3) Edges cannot be
added when nodes are not added
4) Indent Layout does
not work.
5) Sample 2 does not
work as some nodes are not assigned any location, and are hence out of bounce.
*/
Modified: cytoscapeweb/branches/gsoc2010/demos/src/GBEB/Mesh.as
===================================================================
--- cytoscapeweb/branches/gsoc2010/demos/src/GBEB/Mesh.as 2010-07-10
17:17:21 UTC (rev 20895)
+++ cytoscapeweb/branches/gsoc2010/demos/src/GBEB/Mesh.as 2010-07-10
17:17:52 UTC (rev 20896)
@@ -3,9 +3,9 @@
import flare.vis.data.Data;
import flare.vis.data.DataSprite;
import flare.vis.data.EdgeSprite;
+ import flare.vis.data.NodeSprite;
import flash.display.Sprite;
- import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
@@ -15,15 +15,21 @@
////////////////////////////////////////////////////////////////
public class Mesh extends Sprite
{
- public var _grid:Array; //stores the
array of Shapes
+ public var grid:Array; //stores the array of
Shapes
public var gridSize:int = 20; // size of initial bounding grid,
in pixel
+ public var _mesh:Data = new Data(); //stores the actual edges
and nodes of the points for the mesh
+ public var nonRedundantShapeIndexArray:Array = new Array(); //
Stores Shape that actually contain meshEdges and have a general direction
+ private var _data:Data;
+ private var _meshEdgeArray:Array; //Stores the array of
meshEdges for processing
private var _bounds:Rectangle = new Rectangle(); // stores the
bound of the visualisation
- private var _data:Data;
+ private var _originalGrid:Array; //stores the array of Shapes
at maximum resolution
private var angleResolution:int = 15; //stores the angle
resolution needed to resolve grid joining.
private var edgeCounter:int = 1;
+ private const _meshNodesMinDistance:Number = 2.0; //Defines the
minimum distnance between any 2 nodes in the mesh
+
public var numGridsX:int = -1;
public var numGridsY:int = -1;
@@ -36,11 +42,36 @@
public function Mesh (data:Data):void
{
_data = data;
- _grid = new Array();
+ grid = new Array();
}
+ public function cleanup():void
+ {
+ grid = null;
+ nonRedundantShapeIndexArray = null;
+
+ cleanupMeshEdges();
+
+ _data = null;
+ _mesh = null;
+ _meshEdgeArray = null;
+ _bounds = null;
+ _originalGrid = null;
+
+ }
+ private function cleanupMeshEdges():void
+ {
+ for each (var edge:EdgeSprite in
_data.edges)
+ {
+ var props:* =
edge.props.GBEBProperties;
+ if(props != null) props =
null;
+ }
+ }
+
+
// Follows the step below to generate a Mesh
+ // 0. Assign GBEBProperty to all edges
// 1. Generate a uniform grid of 20pix per grid for the entire
bound
// 2. For each grid, detect what edges are inside and store
them in shape | There is no need to
// store nodes at all.
@@ -65,7 +96,15 @@
assignDataToGrid();
+ //changeresolution();
+
+ _data.edges.visit(assignProps);
+
mergeShapeUsingPrimaryDirections();
+
+ generateMeshEdges();
+
+ addControlPointsToAll();
//_grid = mergeShape(angleResolution);
@@ -75,8 +114,16 @@
//trace("Mesh: Testing testIntersection: " +
testIntersection(new Rectangle(-1,-1,5,5), new Point(2,2), new Point(10,5)));
//testing the intersection of glancing contact
}
+
//////////////////////////////////////////////////////////////////////////////////////////////
+ //Step 0: function to assign GBEB Properties to each mesh Edge
that is used for controling them
+
/////////////////////////////////////////////////////////////////////////////////////////////
+ public function assignProps(e:EdgeSprite):void
+ {
+ var prop:GBEBProperty = e.props.GBEBProperty;
+ prop = new GBEBProperty();
+ e.props.GBEBProperty = prop;
+ }
-
//////////////////////////////////////////////
//Step 1: function to generate the grid from scratch
//////////////////////////////////////////////
@@ -98,7 +145,7 @@
while( _counterY <= numGridsY ) //-22
{
- while( _counterX <= numGridsX - 10)// -20
+ while( _counterX <= numGridsX )// -20
{
var _shape:Shape = new Shape();
@@ -126,7 +173,7 @@
}
- _grid = _newGrid;
+ grid = _newGrid;
return void;
}
@@ -142,7 +189,7 @@
{
if(d != null) _data = d;
- if(_grid.length == 0) return;
+ if(grid.length == 0) return;
//data.edges.sortBy({"x", "y"});
@@ -159,7 +206,7 @@
trace((edge.source.data["name"]), " (" +
edge.x1 + "," + edge.y1 + ") ", " | "
+ edge.target.data["name"] + " ( " +
edge.x2 + "," + edge.y2 + ")");
- for each(var _shape:Shape in _grid)
+ for each(var _shape:Shape in grid)
{
for each (var __grid:Rectangle in
_shape.storedGrids)
@@ -185,7 +232,7 @@
}// end of huge for loop
- for each( var _shape:Shape in _grid)
+ for each( var _shape:Shape in grid)
{
_shape.computeDirection();
}
@@ -274,7 +321,7 @@
var iterationIndexArray:Array = new Array(); //this
array stores the index of the shapes that
//needs to be iterated through by the merged Shapes
function
- for each (var s1:Shape in _grid)
+ for each (var s1:Shape in grid)
{
if(s1.direction != -1)
{
@@ -345,7 +392,7 @@
}
- return null; // to be edited.
+ return null;
}
@@ -388,6 +435,8 @@
{
// trace("Mesh: MergeShape: is called!");
+ var e2:EdgeSprite;
+
if(s1 == null && s2 == null) return
null;
if(s2 == null) return s1;
if(s1 == null) return s2;
@@ -396,7 +445,9 @@
while(s2.storedDataEdges.length != 0)
{
//transfers the edges from s2
to s1
-
s1.storedDataEdges.push(s2.storedDataEdges.pop());
+ e2 = s2.storedDataEdges.pop();
+
+
if(s1.storedDataEdges.indexOf(e2) == -1) s1.storedDataEdges.push(e2);
}
//transfer storedGrid Index
@@ -406,7 +457,7 @@
for each (var p:Point in s2.gridIndex)
{
- _grid[indexFromPoint(p)] = s1;
+ grid[indexFromPoint(p)] = s1;
s1.gridIndex.push(p);
//trace("Mesh: MergeShape:
Point "+ p.toString() + " is added to " + s1.gridIndex[0]);
}
@@ -422,9 +473,476 @@
return s1;
}
+
+
+ //////////////////////////////////////////////
+ // Step 4a. Obtain _meshEdges (1 for each Shape), and their respective
nodes.
+ // The direction variable in each shape with a majority direction gives
the gradient of the mesh edge passing through the particular shape.
+ // The Mesh edge needs an intersection point within the shape and this
intersection point is precisely the
+ // centroid of the shape.
+ //////////////////////////////////////////////
+
+ public function generateMeshEdges():void
+ {
+ if(grid == null) return;
+
+ var gradient:Number = 1;
+ var meshEdge:EdgeSprite = new EdgeSprite();
+ var currentGrid:Point = new Point(-1,-1); //stores the index of
the grid that is currently being tested for intersection
+
+ _meshEdgeArray = new Array();
+
+ trace("Mesh: Generate meshEdges: running!");
+
+ generateNonRedundantShapeIndexArray();
+
+ for each (var p1:Point in nonRedundantShapeIndexArray)
+ {
+ var s1:Shape = returnShapeFromIndex(p1);
+ var angleOfLine:Number = 0;
+
+ if(s1.direction == -1) continue;
+ //only shapes with a main direction will continue until
here.
+
+ //trace("Mesh: GenerateMeshEdges: Gradient Calculation:
for " + s1.direction + " gradient = " + Math.tan((s1.direction / 180) *
Math.PI));
+ gradient = -1 / Math.tan((s1.direction / 180) *
Math.PI);
+
+ //angleOfLine = Math.atan(gradient) * 180 / Math.PI;
+ //trace("Mesh: Testing Gradient: Edge " + cycles + " |
direction " + s1.direction + " | Gradient:" + gradient + " |Angle: " +
angleOfLine);
+
+ s1.centroid = findCentroid(s1);
+
+ //trace("Mesh: Generate meshEdges: " +
returnIndexFromXY(s1.centroid.x, s1.centroid.y).toString() + " | " +
s1.centroid.toString());
+ meshEdge =
generateLineFromPointAndGradient(s1.centroid, gradient);
+
+ currentGrid = returnIndexFromXY(s1.centroid.x,
s1.centroid.y);
+
+ if( currentGrid.x < 0 || currentGrid.y < 0 ||
currentGrid.x > numGridsX || currentGrid.y > numGridsY ) continue;
+
+ meshEdge.source = new NodeSprite(); meshEdge.source.x =
meshEdge.x1; meshEdge.source.y = meshEdge.y1;
+
+ meshEdge.target = new NodeSprite(); meshEdge.target.x =
meshEdge.x2; meshEdge.target.y = meshEdge.y2;
+
+ meshEdge.target.name = meshEdge.source.name =
(cycles).toString();
+ meshEdge.name = (cycles++).toString();
+
+ getNodesForMeshEdge(returnIndexFromXY(s1.centroid.x,
s1.centroid.y), meshEdge, meshEdge.source, "None");
+
+
+ //trace("Mesh: GenerateMeshEdges: Assigning names: " +
meshEdge.x1);
+
+ s1.meshEdge = meshEdge;
+
+ if(meshEdge != null)
+ {
+ _meshEdgeArray.push(meshEdge);
+ _mesh.addNode(meshEdge.source);
+ _mesh.addNode(meshEdge.target);
+ _mesh.addEdge(meshEdge);
+
+ //_mesh.edges.add(meshEdge); //adding the mesh
edge to datalist; technically, I can use the _mesh.edge.visit function to runs
+ //mergeNodes_All(), but it might be quite messy
as I have to visit the list of edges in the fashion of bubble sort.
+ }
+ }
+ //trace("Mesh: GenerateMeshEdges: " + _mesh.edges);
+ trace("Mesh: GenerateMeshEdes: No. of meshEdges generated = " +
_meshEdgeArray.length + " | Nodes " + _mesh.nodes.length + " | Edges" +
_mesh.edges.length);
+
+
+
+ //mergeNodes_All(); //huge sub-routine to merge all neigbouring
nodes.
+
+
+ return;
+ }
+
+ //This will cause the edge to shift.
+ //Step 4a.1 :: Recurrsive function. Takes in
the grid index and an edge sprite to return an edge that is assigned with nodes
+ //the nodes are the found at the boundary of
each Shape. It uses the last prevDir variable to pass on the information on
which direction the
+ //edge came from, in order not to get a double
hit. (For example, if the edge passes through the top of the prev grid, it
would definitely pass
+ //through the bottom of the top adjoining grid,
but that is not the next intersection that we are interested to find). This
function also handles
+ //the special case where the gradient is 0 or
infinity.
+ private function
getNodesForMeshEdge(currentGridIndex:Point, edge:EdgeSprite, node:NodeSprite,
prevDir:String):NodeSprite
+ {
+ //trace("Mesh: getNodesForMeshEdge is
running! EdgeNo :" + edge.name + " | CurrentGridIndex: " + currentGridIndex, "|
source", edge.x1,
+ // edge.y1, "| target", edge.x2,
edge.y2);
+ var currentGridXY:Point =
returnXYFromIndex(currentGridIndex);
+ var currentShape:Shape =
returnShapeFromIndex(currentGridIndex);
+ var newGrid:Point = new Point(0,0);
+ var grid:Rectangle = new
Rectangle(currentGridXY.x, currentGridXY.y, gridSize, gridSize);
+
+ var intersectionPoint:Point;
+
+ var p1:Point = new Point(edge.x1,
edge.y1); //stores the x,y coordinates of the source of edgeNode temporarily.
+ var p2:Point = new Point(edge.x2,
edge.y2); //stores the x,y coordinates of the target of edgeNode temporarily.
+
+ intersectionPoint =
intersectsHorizontal(grid.topLeft, topRight(grid), p1, p2); //check if the edge
intersects with the top
+ if(intersectionPoint != null && prevDir
!= "Bottom" )
+ {
+ newGrid.x = currentGridIndex.x;
newGrid.y = currentGridIndex.y - 1;
+ node.x = intersectionPoint.x,
node.y = intersectionPoint.y;
+
if(searchShapeForIndex(currentShape, newGrid))
+ {
+ node =
getNodesForMeshEdge(newGrid, edge, node, "Top");
+ } else if (prevDir != "None") {
+ return node;
+ }
+ }
+
+
+ //I am overloading this huge recurrsive
to handle both the source and target nodes together. This function basically
ask if
+ //this is the first time the function
is called for an edge. If it is, and that if edge.source has been moved to an
intersection point
+ //it will continue to work with the
edge.target as node.
+ //Notes: It doesnt matter that the
variable node is coninuously assigned to edge.target.
+ if(prevDir == "None" &&
isSourceNodeAssigned(edge))
+ {
+ node = edge.target;
+ }
+
+ //check if the edge intersects with the
bottomEdge
+ intersectionPoint =
intersectsHorizontal(bottomLeft(grid), grid.bottomRight, p1, p2);
+
+ //trace("Mesh: getNodesForMeshEdge:
IntersectionPoint: " + intersectionPoint.toString());
+
+ if(intersectionPoint != null && prevDir
!= "Top" )
+ {
+ newGrid.x = currentGridIndex.x;
newGrid.y = currentGridIndex.y + 1;
+ node.x = intersectionPoint.x,
node.y = intersectionPoint.y;
+
if(searchShapeForIndex(currentShape, newGrid))
+ {
+ node =
getNodesForMeshEdge(newGrid, edge, node,"Bottom");
+ } else if (prevDir != "None") {
+ return node;
+ }
+ }
+
+
+
+ if(prevDir == "None" &&
isSourceNodeAssigned(edge))
+ {
+ node = edge.target;
+ }
+
+ //check if the edge intersects with the
leftEdge
+ intersectionPoint =
intersectsVertical(grid.topLeft, bottomLeft(grid), p1, p2);
+
+
+ if(intersectionPoint != null && prevDir
!= "Right" )
+ {
+ newGrid.x = currentGridIndex.x
- 1; newGrid.y = currentGridIndex.y;
+ node.x = intersectionPoint.x,
node.y = intersectionPoint.y;
+
if(searchShapeForIndex(currentShape, newGrid))
+ {
+ node =
getNodesForMeshEdge(newGrid, edge, node, "Left");
+ } else if (prevDir != "None") {
+ return node;
+ }
+ }
+
+ //I have included the whole statement
for consistency. Technically, if can do without the right portion after "&&")
+ //as each grid is guranteed to have 2
intersections points, so by the 4th assignment must belong to the edge.
+ if(prevDir == "None" &&
isSourceNodeAssigned(edge))
+ {
+ node = edge.target;
+ }
+
+ //check if the edge intersects with the
rightEdge
+ intersectionPoint =
intersectsVertical(topRight(grid), grid.bottomRight, p1, p2);
+
+ if(intersectionPoint != null && prevDir
!= "Left" )
+ {
+ newGrid.x = currentGridIndex.x
+ 1; newGrid.y = currentGridIndex.y;
+ node.x = intersectionPoint.x,
node.y = intersectionPoint.y;
+
if(searchShapeForIndex(currentShape, newGrid))
+ {
+ node =
getNodesForMeshEdge(newGrid, edge, node, "Right");
+ } else if (prevDir != "None") {
+ return node;
+ }
+ }
+
+ //trace("Mesh:
getNodesForMeshEdge: " + centroid.toString() + "(It is only suppose to fall
through once per edge!)"); //since each centroid is unique
+ return node;
+ }
+
+
+ //Step 4a.1 :: Support. This
function is basically asking if the previous
+ // the intersections for the "source
Node" has been found.
+ //Worry about flash arithmetic? (what
if the change is very small? what if "==" does not
+ private function
isSourceNodeAssigned(edge:EdgeSprite):Boolean
+ {
+ if( Math.abs(edge.source.x -
edge.x1) < 0.5 && Math.abs(edge.source.y - edge.y1) < 0.5)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ //Step 4a.2 ::This function is a clean
up function which generates an array of nonRedundant index of Unique shapes
that have
+ //a major direction which helps in
further processesing of these edges
+ private function
generateNonRedundantShapeIndexArray():Array
+ {
+ nonRedundantShapeIndexArray =
new Array();
+
+ for each (var s1:Shape in grid)
+ {
+ if(s1.direction != -1
&& nonRedundantShapeIndexArray.indexOf(s1.gridIndex[0]) == -1)
+ {
+
nonRedundantShapeIndexArray.push(s1.gridIndex[0]);
+ }
+ }
+
+ return
nonRedundantShapeIndexArray;
+ }
+
+ //Step 4a.3 ::this function is usually
very complication if the Shapes are irregular. However, since I have
+ //define the primitive shape as a
square a very special case of shapes, this function becomes
+ //quite simple. (=
+ private function
findCentroid(s:Shape):Point
+ {
+ if(s == null) return null;
+
+ var numPoints:int = 0;
+ var xCoor:Number = 0;
+ var yCoor:Number = 0;
+ var xyCoor:Point = new
Point(0,0);
+
+ for each (var p:Point in
s.gridIndex)
+ {
+ xyCoor =
returnXYFromIndex(p);
+
+ xCoor += (xyCoor.x +
0.5 * gridSize);
+ yCoor += (xyCoor.y +
0.5 * gridSize);
+
+ numPoints++;
+ }
+
+ if(numPoints == 0) return null;
+
+ return new Point( (xCoor /
numPoints), ( yCoor / numPoints) );
+
+ //check if centroid lies within
the Area of the shape?
+ //yes, I have have to. Should I
assign the closet grid?
+
+ //return returnXYFromIndex( new
Point( (xIndex / numPoints), (yIndex / numPoints) ));
+ }
+
+ //Step 4a.4 This function takes in the
point in which the line passes through, and the gradient of the lines
+ //and return a straight EdgeSprite that
is defined by the the bottom left (source coordinates) to the top-right (target
corrdinates),
+ //where the source and target are the
intersection of the line with the boundies of the graph
+ private function
generateLineFromPointAndGradient(p:Point, gradient:Number): EdgeSprite
+ {
+ var pointsArray:Array = new
Array(); //temp storage for the target and source
+ var edgeSprite:EdgeSprite = new
EdgeSprite();
+ var isSourceAssigned:Boolean =
false;
+
+ //trace("Mesh:
generateLineFromPoint: Checking for gradient :" + gradient + " at " +
p.toString());
+
+ //boundary conditions
+ //if (gradient ==
Number.POSITIVE_INFINITY || gradient == Number.NEGATIVE_INFINITY)
+ if( gradient > 500 || gradient
< -500)
+ {
+ if(Math.abs(p.x -
_bounds.x) <= 0.01)
+ {
+
pointsArray.push(new Point(_bounds.x, _bounds.y));
+
pointsArray.push(new Point((_bounds.width), (_bounds.height)));
+ } else {
+
pointsArray.push(new Point(p.x, _bounds.y));
+
pointsArray.push(new Point(p.x, _bounds.height));
+ }
+
+ //trace("Mesh:
generateLineFromPoint: Checking for gradient = infinity");
+ } else if ( gradient > -0.01 &&
gradient < 0.01)
+ {
+ //if gradient is
nearing 0
+ if(Math.abs(p.y -
_bounds.y) <= 0.01)
+ {
+
pointsArray.push(new Point(_bounds.x, _bounds.y));
+
pointsArray.push(new Point((_bounds.width), _bounds.y));
+ } else {
+
pointsArray.push(new Point(_bounds.x, p.y));
+
pointsArray.push(new Point(_bounds.width, p.y));
+ }
+
+ //trace("Mesh:
generateLineFromPoint: Checking for gradient = 0");
+ } else {
+
+ //gradient = -1
/gradient;
+
+
pointsArray.push(intersectionWithVertical(_bounds.x, p.x, p.y, gradient));
//with left boundary
+
pointsArray.push(intersectionWithVertical(_bounds.width + _bounds.x, p.x, p.y,
gradient)); //with right boundary
+
pointsArray.push(intersectionWithHorizontal(_bounds.y, p.y, p.x, gradient));
//with top boundary
+
pointsArray.push(intersectionWithHorizontal(_bounds.height + _bounds.y, p.y,
p.x, gradient)); //with bottom boundary
+
+ //trace("Mesh:
generateLineFromPoint: Checking for normal gradients...");
+ }
+
+ for each ( var p:Point in
pointsArray)
+ {
+ if( p != null)
+ {
+
if(!isSourceAssigned)
+ {
+
edgeSprite.x1 = p.x;
+
edgeSprite.y1 = p.y;
+
isSourceAssigned = true;
+ } else {
+
edgeSprite.x2 = p.x;
+
edgeSprite.y2 = p.y;
+ }
+
+ //trace("Mesh:
generateLineFromPoint: pointsArray can be assigned " + edgeSprite.x1,
edgeSprite.y1, " | ", edgeSprite.x2, edgeSprite.y2 );
+
+ //for tracing
+ //var
angle:Number = (edgeSprite.y2 - edgeSprite.y1) / (edgeSprite.x2 -
edgeSprite.x1);
+ //angle =
Math.atan(angle) / Math.PI * 180;
+ //trace("Mesh:
generateLineFromPoint:" + cycles + " | angle " + angle);
+ }else {
+ //trace("Mesh:
MeshEdge Generator: PointsArray.length: " + pointsArray.length + " |
NULL!!!!!");
+ }
+ }
+ //trace("Mesh:
generateLineFromPoint: " + edgeSprite.x1 + "!!!!!");
+
+ return edgeSprite;
+ }
+
+ //Step 4a.4.1
This function checks if the edge generated intersects with the vertical
boundary of the _mesh/_bounds boundary. If it does,
+ // it returns
the intersection point. Vice versa for 4a.4.2, except that it checks against
the horizontal boundary.
+ //Does it
handle the case where gradient = 0 or infinity?
+ private
function intersectionWithVertical(xBoundary:Number, x1:Number, y1:Number,
gradient:Number):Point
+ {
+ var
yCoor:Number = (gradient * ( xBoundary - x1)) + y1; //stores the y coordinate
of the intersection point with the vertical boundary
+
+ var
diff:Number = yCoor - y1;//Patch to reflect the gradient about the y -axis;
necessary due to unknown bug
+ yCoor
-= 2 * diff;//Patch to reflect the gradient about the y -axis; necessary due to
unknown bug
+
+ if(
yCoor <= _bounds.y + _bounds.height && yCoor >= _bounds.y) //if intersects
directly at the corner, the vertial function will return the points
+ {
+
return new Point(xBoundary, yCoor);
+ }
+ return
null;
+ }
+
+ //Step 4a.4.2
+ private
function intersectionWithHorizontal(yBoundary:Number, y1:Number, x1:Number,
gradient:Number):Point
+ {
+
gradient = 1/gradient;
+
+ var
xCoor:Number = (gradient * (yBoundary - y1)) + x1;
+
+ var
diff:Number = xCoor - x1;//Patch to reflect the gradient about the y -axis;
necessary due to unknown bug
+ xCoor
-= 2 * diff;//Patch to reflect the gradient about the y -axis; necessary due to
unknown bug
+
+ if(
xCoor < _bounds.x + _bounds.width && xCoor > _bounds.x) //if intersects
directly at the corner, the vertial function will return the points
+ {
+
return new Point(xCoor, yBoundary);
+ }
+ return
null;
+ }
+
+
+ //Step 4b. Merge nodes that are too close
together. ( < x pix )
+ //Since the nodes itself doesnt not contain any
reference to the edges, edges are input instead,
+ //so he edge's parameteres can be altered. It
does a pairwise comparison with all nodes on the mesh
+ public function mergeNodes_All():void
+ {
+ var currEdge:EdgeSprite;
+ var currNode:NodeSprite;
+
+ trace("Mesh: MergeNodes is running.");
+
+ while(_meshEdgeArray.length != 0)
+ {
+ currEdge = _meshEdgeArray.pop();
+ currNode = currEdge.source;
+
+ for each (var edge2:EdgeSprite
in _meshEdgeArray)
+ {
+
if(calculateDistanceBetweenNodes(currNode, edge2.source) <
_meshNodesMinDistance)
+ {
+
mergeNodes_Pairwise(currEdge, "source", edge2, "source");
+ continue;
+ } else if
(calculateDistanceBetweenNodes(currNode, edge2.target) < _meshNodesMinDistance)
+ {
+
mergeNodes_Pairwise(currEdge, "source", edge2, "target");
+ continue;
+ }
+
+ currNode =
currEdge.target;
+
+
if(calculateDistanceBetweenNodes(currNode, edge2.source) <
_meshNodesMinDistance)
+ {
+
mergeNodes_Pairwise(currEdge, "target", edge2, "source");
+ continue;
+ } else if
(calculateDistanceBetweenNodes(currNode, edge2.target) < _meshNodesMinDistance)
+ {
+
mergeNodes_Pairwise(currEdge, "target", edge2, "target");
+ continue;
+ }
+ }
+ }
+
+ }
+
+ //Step 4b.1
This function checks if 2 nodeSprites are too close together. The minimum
distance is
+ //defined by
the const _meshNodesMinDistance
+ private
function calculateDistanceBetweenNodes(node1:NodeSprite,
node2:NodeSprite):Number
+ {
+ var
distance:Number = 0;
+
+ //this
is Pythogoras' Theorem
+
distance = Math.sqrt( Math.pow((node1.x - node2.x), 2) + Math.pow((node1.y -
node2.y), 2));
+ if
(distance < _meshNodesMinDistance) trace("Mesh: CalculateDistanceBetweenNodes:
" + distance, "|",
+
node1.x, node1.y, "||", node2.x, node2.y);
+
+
return distance;
+ }
+
+ //Step 4b.2
this function creates a new node in the intersection between the extension of
the 2 edges
+ private
function mergeNodes_Pairwise(edge1:EdgeSprite, s1:String, edge2:EdgeSprite,
s2:String):void
+ {
+
if(edge1 == null || s1 == null || edge2 == null || s2 == null) return;
+
+ var
a:Point = new Point(edge1.x1, edge1.y1);
+ var
b:Point = new Point(edge1.x2, edge1.y2);
+ var
e:Point = new Point(edge2.x1, edge2.y1);
+ var
f:Point = new Point(edge2.x2, edge2.y2);
+
+ var
ip:Point = GBEB.GeometryUtil.lineIntersectLine( a, b, e, f); //stores the
intersectionPoint
+
+ if( ip
== null ) return;
+
+
trace("Mesh: Clustering Neigbouring Nodes: A new intersectionPoint has been
created at " + ip.toString()
+
+ "\nfrom points (" + edge1[s1].x + "," + edge1[s1].y + ") " + s1 + " of Edge:
" + edge1.name + " and ("
+
+ edge2[s2].x + "," + edge2[s2].y + ") " + s2 + " of Edge: " + edge2.name );
+
+ //below
checks for the particular source/target node of edge2, which will be moved to
the intersection Point
+ //then
it assigns the particular source/target node of edge1 to the previously moved
node. This results in the removal of
+ //nodes
in the graph and reduces its node density, by "clustering" nodes
+
+
(edge2[s2] as NodeSprite).x = ip.x; (edge2[s2] as NodeSprite).y = ip.y
+
+
edge1[s1] = edge2[s2];
+
+ return;
+ }
+
+
+ // Step 5. For each edge in _data, check for their intersection
with _mesh.edge and record these
+ // intersection points as CPand their CP - this is actually
quite a challenge
+ public function addControlPointsToAll():void
+ {
+ for each (var p:Point in nonRedundantShapeIndexArray)
+ {
+ var s:Shape = returnShapeFromIndex(p);
+ s.addControlPoint();
+ }
+ }
+
////////////////////////////////
// Helper functions
////////////////////////////////
@@ -437,19 +955,27 @@
var gridX:int = Math.floor(( x
- _bounds.x) / gridSize );
var gridY:int = Math.floor(( y
- _bounds.y) / gridSize );
- return (_grid[gridY *
(numGridsX - ( 10 - 1) )+ gridX] as Shape); //remove the -20 after trials
+ return (grid[gridY * (numGridsX
- (- 1) )+ gridX] as Shape); //remove the (10-1) after trials
}
public function
returnShapeFromIndex(p:Point):Shape
{
- return (_grid[p.y * (numGridsX
- 9 )+ p.x] as Shape); //remove the -20 after trials
+ return (grid[p.y * (numGridsX +
1 )+ p.x] as Shape); //remove the (10-1) after trials
}
public function
indexFromPoint(p:Point):int
{
- return p.x + p.y * (numGridsX -
9 );
+ return p.x + p.y * (numGridsX +
1 ); //remove the (10-1) after trials
}
+ public function
returnIndexFromXY(x:Number, y:Number):Point
+ {
+ x = Math.floor( (x - _bounds.x)
/ gridSize);
+ y = Math.floor( (y - _bounds.y)
/ gridSize);
+
+ return new Point(x, y);
+ }
+
//returns the actual (x,y) coordinate
from index, returns the top-left point of the grid.
//May return any intermediate value if
the Point p given is not any integer.
public function
returnXYFromIndex(p:Point):Point
@@ -457,6 +983,19 @@
return new Point( (p.x *
gridSize) + _bounds.x , (p.y * gridSize) + _bounds.y );
}
+ //searches the GridIndex array of a
given Shape for an index. Returns true, if the index exist in the Grid Array
+ public function
searchShapeForIndex(s:Shape, index:Point):Boolean
+ {
+ if(s == null) return false;
+
+ for each(var p:Point in
s.gridIndex)
+ {
+ if(p.x == index.x &&
p.y == index.y) return true;
+ }
+
+ return false;
+ }
+
////////////////////////////////
// functions that I am testing out
////////////////////////////////
--
You received this message because you are subscribed to the Google Groups
"cytoscape-cvs" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/cytoscape-cvs?hl=en.
