Author: paperwing
Date: 2012-01-29 19:25:46 -0800 (Sun, 29 Jan 2012)
New Revision: 28154

Modified:
   
csplugins/trunk/toronto/yuedong/paperwing-impl/src/main/java/org/cytoscape/paperwing/internal/rendering/RenderArcEdgesProcedure.java
Log:
refs #647 implementation for self-edges (edges that go from a node to itself) 
completed by inverting the angle of the arc to a reflex angle, so these edges 
should appear as circles that loop back on the node at appropriate rotations 
and radii

Modified: 
csplugins/trunk/toronto/yuedong/paperwing-impl/src/main/java/org/cytoscape/paperwing/internal/rendering/RenderArcEdgesProcedure.java
===================================================================
--- 
csplugins/trunk/toronto/yuedong/paperwing-impl/src/main/java/org/cytoscape/paperwing/internal/rendering/RenderArcEdgesProcedure.java
        2012-01-28 02:08:32 UTC (rev 28153)
+++ 
csplugins/trunk/toronto/yuedong/paperwing-impl/src/main/java/org/cytoscape/paperwing/internal/rendering/RenderArcEdgesProcedure.java
        2012-01-30 03:25:46 UTC (rev 28154)
@@ -48,6 +48,8 @@
        private static final float DOTTED_EDGE_RADIUS = 0.012f;
        private static final float DOTTED_EDGE_SPACING = 0.08f;
        
+       private static final double ARC_EDGE_MINIMUM_RADIUS = 0.1;
+       
        /**
         * The number of straight segments used to approximate a curved edge
         */
@@ -70,6 +72,12 @@
                
                // The total number of edges that connect the pair of nodes.
                protected int totalCoincidentEdges;
+               
+               // Does this edge direct from a node to itself?
+               protected boolean selfEdge;
+               
+               // TODO: Consider storing the edge start and end coordinates in 
this class
+               // via the analyzeEdges() method
        }
        
        public RenderArcEdgesProcedure() {
@@ -94,6 +102,8 @@
                int nodeCount = networkView.getModel().getNodeCount();
                CyEdge edge;
                
+               boolean selfEdge;
+               
                for (View<CyEdge> edgeView : networkView.getEdgeViews()) {      
                
                        edge = edgeView.getModel();
                        
@@ -116,10 +126,18 @@
                        
                        pairCoincidenceCount.put(identifier, edgeNumber);
                        
+                       // Check if the edge leads from a node to itself
+                       if (sourceIndex == targetIndex) {
+                               selfEdge = true;
+                       } else {
+                               selfEdge = false;
+                       }
+                       
                        EdgeViewContainer container = new EdgeViewContainer();
                        container.edgeView = edgeView;
                        container.pairIdentifier = identifier;
                        container.edgeNumber = edgeNumber;
+                       container.selfEdge = selfEdge;
                        
                        edgeViewContainers.add(container);
                }
@@ -158,15 +176,20 @@
                        start = obtainCoordinates(edge.getSource(), 
networkView, distanceScale);
                        end = obtainCoordinates(edge.getTarget(), networkView, 
distanceScale);
                        
-                       // TODO: Check for cases where the edge goes from a 
node to itself
-                       if (start != null && end != null && end.distance(start) 
>= MIN_LENGTH) {
+                       if (start != null && end != null && 
+                                       (end.distance(start) >= MIN_LENGTH || 
container.selfEdge)) {
                                
                                // Load name for edge picking
                                gl.glLoadName(edge.getIndex());
                                
+                               // Determine the circle radius as well as 
rotation angle for the edge
                                double[] edgeMetrics = findArcEdgeMetrics(
-                                               start, end, 
container.edgeNumber, container.totalCoincidentEdges);
+                                               start, end, 
container.edgeNumber, 
+                                               container.totalCoincidentEdges, 
container.selfEdge);
                                
+                               // Check if the edge leads from a node to 
itself, ie. requires arc inversion
+                               boolean invert = container.selfEdge;
+                               
                                // General points along the arc
                                Vector3[] points;
                                
@@ -174,20 +197,20 @@
                                if 
(edgeView.getVisualProperty(RichVisualLexicon.EDGE_LINE_TYPE)
                                                == 
LineTypeVisualProperty.EQUAL_DASH) {
                                        points = generateSparseArcCoordinates(
-                                                       start, end, 
edgeMetrics[0], edgeMetrics[1], DASHED_EDGE_SPACING);
+                                                       start, end, 
edgeMetrics[0], edgeMetrics[1], DASHED_EDGE_SPACING, invert);
                                
                                        drawDashedArc(gl, points);
                                } else if 
(edgeView.getVisualProperty(RichVisualLexicon.EDGE_LINE_TYPE)
                                                == LineTypeVisualProperty.DOT) {
                                        points = generateSparseArcCoordinates(
-                                                       start, end, 
edgeMetrics[0], edgeMetrics[1], DOTTED_EDGE_SPACING);
+                                                       start, end, 
edgeMetrics[0], edgeMetrics[1], DOTTED_EDGE_SPACING, invert);
                                
                                        drawDottedArc(gl, points);
                                        
                                // Draw regular edges for the catch-all case
                                } else {
                                        points = generateArcCoordinates(
-                                                       start, end, 
edgeMetrics[0], edgeMetrics[1], NUM_SEGMENTS);
+                                                       start, end, 
edgeMetrics[0], edgeMetrics[1], NUM_SEGMENTS, invert);
                                        
                                        drawRegularArc(gl, points);
                                }
@@ -243,52 +266,7 @@
                
                return coordinates;
        }
-       
-
-       /**
-        * Generates a set of edge-specific arc coordinates, so that the set of 
drawing coordinates
-        * representing the edge are made to not coincide with other edges that 
connect the same pair of nodes
-        * 
-        * The first of the coordinates is equal to the starting position, and 
the last is equal to the
-        * terminal position.
-        * 
-        * @param start The starting position of the edge
-        * @param end The terminal position of the edge
-        * @param edgeNumber The index of this edge out of all the edges that 
connect the same pair of nodes,
-        * with 1 meaning it is the first of all the edges
-        * @param totalCoincidentEdges The total number of edges that connect 
the same nodes as this edge
-        * @return
-        */
-       private Vector3[] generateEdgeSpecificArcCoordinates(
-                       Vector3 start, Vector3 end, int edgeNumber, int 
totalCoincidentEdges) {
                
-               // If this is the only edge connecting these nodes, make it 
straight and simply
-               // return the endpoints
-               if (totalCoincidentEdges == 1 && edgeNumber == 1) {
-                       return new Vector3[] {start.copy(), end.copy()};
-               }
-               
-               // Level 1 has 2^2 - 1^1 = 3 edges, level 2 has 3^3 - 2^2 = 5, 
level 3 has 7
-               int edgeLevel = (int) (Math.sqrt((double) edgeNumber));
-               int maxLevel = (int) (Math.sqrt((double) totalCoincidentEdges));
-               
-               int edgesInLevel = edgeLevel * 2 + 1;
-               
-               // Smaller edge level -> greater radius
-               double curvedEdgeRadius = start.distance(end) * (0.5 + (double) 
1.5 / Math.pow(edgeLevel, 2));
-               
-               // The outmost level is usually not completed
-               if (edgeLevel == maxLevel) {
-                       edgesInLevel = (int) (totalCoincidentEdges - 
Math.pow(maxLevel, 2) + 1);
-               }
-               
-               double edgeRadialAngle = (double) (edgeNumber - 
Math.pow(edgeLevel, 2)) / edgesInLevel * Math.PI * 2;
-
-               // return generateArcCoordinates(start, end, curvedEdgeRadius, 
edgeRadialAngle, NUM_SEGMENTS);
-               
-               return generateSparseArcCoordinates(start, end, 
curvedEdgeRadius, edgeRadialAngle, DASHED_EDGE_SPACING);
-       }
-       
        /**
         * Return a 2-tuple containing the appropriate radius for the circular 
edge arc, as well
         * as how much it should be rotated in the node-to-node displacement 
axis.
@@ -298,9 +276,11 @@
         * @param edgeNumber The index of this edge amongst the edges that 
connect the same pair of nodes,
         * starting from 1
         * @param totalCoincidentEdges The total number of edges that connect 
this pair of nodes
+        * @param selfEdge Whether or not the edge leads from a node to itself
         */
-       private double[] findArcEdgeMetrics(Vector3 start, Vector3 end, int 
edgeNumber, int totalCoincidentEdges) {
-
+       private double[] findArcEdgeMetrics(Vector3 start, Vector3 end, 
+                       int edgeNumber, int totalCoincidentEdges, boolean 
selfEdge) {
+               
                // Level 1 has 2^2 - 1^1 = 3 edges, level 2 has 3^3 - 2^2 = 5, 
level 3 has 7
                int edgeLevel = (int) (Math.sqrt((double) edgeNumber));
                int maxLevel = (int) (Math.sqrt((double) totalCoincidentEdges));
@@ -308,8 +288,14 @@
                int edgesInLevel = edgeLevel * 2 + 1;
                
                // Smaller edge level -> greater radius
-               double curvedEdgeRadius = start.distance(end) * (0.5 + (double) 
1.5 / Math.pow(edgeLevel, 2));
+               double curvedEdgeRadius;
                
+               if (selfEdge) {
+                       curvedEdgeRadius = start.distance(end) * (0.5 + 
(double) 1.5 / Math.pow(edgeLevel, 2));
+               } else {
+                       curvedEdgeRadius = ARC_EDGE_MINIMUM_RADIUS * (0.5 + 
(double) 1.5 / Math.pow(edgeLevel, 2));
+               }
+               
                // The outmost level is usually not completed
                if (edgeLevel == maxLevel) {
                        edgesInLevel = (int) (totalCoincidentEdges - 
Math.pow(maxLevel, 2) + 1);
@@ -332,11 +318,14 @@
         * the right-hand rule, from the positive z-axis.
         * @param segments The number of straight segments to divide the arc 
into. The number
         * of points returned is equal to the number of segments + 1. Must be 
at least 1.
+        * @param invert When set to true, generates the coordinates of the arc 
spanning greater
+        * than 180 degrees of the circle, instead of the smaller obtuse or 
acute arc. This
+        * can be useful for drawing edges from an edge to itself.
         * @return An array of position vectors representing the arc, where the 
first point
         * is equal to the start of the arc, and the last point is equal to the 
end of the arc.
         */
        private Vector3[] generateArcCoordinates(Vector3 start, Vector3 end, 
-                       double radius, double angle, int segments) {
+                       double radius, double angle, int segments, boolean 
invert) {
                
                Vector3[] arcCoordinates = new Vector3[segments + 1];
                
@@ -349,6 +338,11 @@
                
                double rotation = arcAngle / segments;
                
+               if (invert) {
+                       arcAngle = 2 * Math.PI - arcAngle;
+                       rotation = -rotation;
+               }
+               
                for (int i = 0; i < segments; i++) {
                        arcCoordinates[i] = 
circleCenter.plus(startOffset.rotate(rotationNormal, rotation * i));
                }
@@ -359,9 +353,26 @@
                return arcCoordinates;
        }
        
-       // Generate points along the arc, governed by the distance between 
points on the arc
+       /**
+        * Similar to generateArcCoordinates(), but the result is governed by 
the distance
+        * between points rather than the number of points. Also, the result is 
calculated
+        * such that the points are distributed symmetrically between the 
starting
+        * and end positions.
+        * 
+        * @param start The starting point
+        * @param end The ending point
+        * @param radius The radius of the circle whose arc the points lie on
+        * @param angle The rotation angle of the points along the start-to-end 
displacement
+        * axis, by the right-hand rule from the positive z-axis
+        * @param distance The desired distance between the points
+        * @param invert Whether to invert the arc on the circle such that 
instead of generating
+        * points along the smaller, less than or equal to 180 degrees arc, the 
arc spanning
+        * more than 180 degrees is used. This is useful for drawing edges from 
a node to itself.
+        * @return An array of points along the arc, where the first and last 
points correspond
+        * to the given starting and ending positions.
+        */
        private Vector3[] generateSparseArcCoordinates(Vector3 start, Vector3 
end,
-                       double radius, double angle, double distance) {
+                       double radius, double angle, double distance, boolean 
invert) {
                
                Vector3 circleCenter = findCircleCenter(start, end, radius, 
angle);
                Vector3 startOffset = start.subtract(circleCenter);
@@ -377,6 +388,12 @@
                
                int halfIncrements = (int) (arcAngle / segmentAngle / 2);
                
+               // Check if it is necessary to invert the arc in the circle, 
going from an angle
+               // less than or equal to 180 degrees to a reflex angle
+               if (invert) {
+                       arcAngle = 2 * Math.PI - arcAngle;
+                       segmentAngle = -segmentAngle;
+               }
                
                // Add 2 to include the start and end points
                Vector3[] arcCoordinates = new Vector3[2 * halfIncrements + 2]; 

-- 
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.

Reply via email to