Author: paperwing
Date: 2012-03-15 15:00:37 -0700 (Thu, 15 Mar 2012)
New Revision: 28557

Modified:
   
csplugins/trunk/toronto/yuedong/paperwing-impl/src/main/java/org/cytoscape/paperwing/internal/cytoscape/edges/AugmentedEdgeContainer.java
   
csplugins/trunk/toronto/yuedong/paperwing-impl/src/main/java/org/cytoscape/paperwing/internal/cytoscape/edges/EdgeAnalyser.java
Log:
Completed first version of EdgeAnalyzer class, used to prevent unnecessary edge 
segment coordinate recalculation for all edges in the current network view. 
Currently needs a way to determine if the current set of analyzed edge data is 
outdated and needs to be recalculated. This could be done by using the frame 
number or the time elapsed since the last edge coordinate calculation.

Modified: 
csplugins/trunk/toronto/yuedong/paperwing-impl/src/main/java/org/cytoscape/paperwing/internal/cytoscape/edges/AugmentedEdgeContainer.java
===================================================================
--- 
csplugins/trunk/toronto/yuedong/paperwing-impl/src/main/java/org/cytoscape/paperwing/internal/cytoscape/edges/AugmentedEdgeContainer.java
   2012-03-15 21:13:00 UTC (rev 28556)
+++ 
csplugins/trunk/toronto/yuedong/paperwing-impl/src/main/java/org/cytoscape/paperwing/internal/cytoscape/edges/AugmentedEdgeContainer.java
   2012-03-15 22:00:37 UTC (rev 28557)
@@ -29,5 +29,97 @@
        // Should this edge be a straight edge because it is the only edge 
between 2 nodes?
        private boolean straightEdge;
        
+       // Does this edge have enough length to be drawn?
+       private boolean sufficientLength;
+       
+       private Vector3 start;
+       private Vector3 end;
+       
        private Vector3[] coordinates;
+
+       public View<CyEdge> getEdgeView() {
+               return edgeView;
+       }
+
+       public void setEdgeView(View<CyEdge> edgeView) {
+               this.edgeView = edgeView;
+       }
+
+       public long getPairIdentifier() {
+               return pairIdentifier;
+       }
+
+       public void setPairIdentifier(long pairIdentifier) {
+               this.pairIdentifier = pairIdentifier;
+       }
+
+       public int getEdgeNumber() {
+               return edgeNumber;
+       }
+
+       public void setEdgeNumber(int edgeNumber) {
+               this.edgeNumber = edgeNumber;
+       }
+
+       public int getTotalCoincidentEdges() {
+               return totalCoincidentEdges;
+       }
+
+       public void setTotalCoincidentEdges(int totalCoincidentEdges) {
+               this.totalCoincidentEdges = totalCoincidentEdges;
+       }
+
+       public boolean isSelfEdge() {
+               return selfEdge;
+       }
+
+       public void setSelfEdge(boolean selfEdge) {
+               this.selfEdge = selfEdge;
+       }
+
+       public boolean isStraightEdge() {
+               return straightEdge;
+       }
+
+       public void setStraightEdge(boolean straightEdge) {
+               this.straightEdge = straightEdge;
+       }
+
+       public void setSufficientLength(boolean sufficientLength) {
+               this.sufficientLength = sufficientLength;
+       }
+
+       public boolean isSufficientLength() {
+               return sufficientLength;
+       }
+       
+       public Vector3[] getCoordinates() {
+               return coordinates;
+       }
+
+       public void setCoordinates(Vector3[] coordinates) {
+               this.coordinates = coordinates;
+       }
+       
+       public void setStart(Vector3 start) {
+               this.start = start;
+       }
+
+       public Vector3 getStart() {
+               return start;
+       }
+
+       public void setEnd(Vector3 end) {
+               this.end = end;
+       }
+
+       public Vector3 getEnd() {
+               return end;
+       }
+
+
+
+       
+       
+       
 }

Modified: 
csplugins/trunk/toronto/yuedong/paperwing-impl/src/main/java/org/cytoscape/paperwing/internal/cytoscape/edges/EdgeAnalyser.java
===================================================================
--- 
csplugins/trunk/toronto/yuedong/paperwing-impl/src/main/java/org/cytoscape/paperwing/internal/cytoscape/edges/EdgeAnalyser.java
     2012-03-15 21:13:00 UTC (rev 28556)
+++ 
csplugins/trunk/toronto/yuedong/paperwing-impl/src/main/java/org/cytoscape/paperwing/internal/cytoscape/edges/EdgeAnalyser.java
     2012-03-15 22:00:37 UTC (rev 28557)
@@ -7,9 +7,14 @@
 import java.util.Set;
 
 import org.cytoscape.model.CyEdge;
+import org.cytoscape.model.CyNode;
 import org.cytoscape.paperwing.internal.geometric.Vector3;
+import org.cytoscape.paperwing.internal.tools.EdgeCoordinateCalculator;
+import org.cytoscape.paperwing.internal.tools.GeometryToolkit;
 import org.cytoscape.view.model.CyNetworkView;
 import org.cytoscape.view.model.View;
+import org.cytoscape.view.presentation.property.BasicVisualLexicon;
+import org.cytoscape.view.presentation.property.LineTypeVisualProperty;
 
 /**
  * This class is responsible for analyzing the current set of edges in the 
network and
@@ -17,35 +22,76 @@
  */
 public class EdgeAnalyser {
        
-       /** A set of {@link AugmentedEdgeContainer} objects containing extra 
generated data relating to each
+       private static final double MIN_LENGTH = Double.MIN_NORMAL;
+       
+       private static final float DASHED_EDGE_SPACING = 0.07f;
+       
+       private static final float DOTTED_EDGE_SPACING = 0.057f;
+       
+       private static final double ARC_SELF_EDGE_MINIMUM_RADIUS = 0.055; // 
0.045 default feb 7, 2012
+       private static final double ARC_SELF_EDGE_RADIUS_FACTOR = 0.008; // 
0.007 default feb 7, 2012
+       private static final double ARC_SELF_EDGE_EXPONENTIAL_BASE = 1.25;
+       
+       /**
+        * The number of straight segments used to approximate a curved edge
+        */
+       private static final int NUM_SEGMENTS = 8;
+       
+       /** 
+        * A set of {@link AugmentedEdgeContainer} objects containing extra 
generated data relating to each
         * edge as well as a reference to the edge it contains
         */
-       private Collection<AugmentedEdgeContainer> edgeContainers;
+       private Map<View<CyEdge>, AugmentedEdgeContainer> edgeContainers;
        
        /** The frame number that the generated edge data is current for */
        private Long currentFrame;
        
-       // Analyze the network to obtain whether each edge is connecting 2 
nodes that
-       // are already connected by other nodes
-       // Maybe add an optimization so we only have to re-analyze the network 
each time it changes?
-       private Set<EdgeViewContainer> analyzeEdges(CyNetworkView networkView, 
double distanceScale) {
+       public EdgeAnalyser() {
+               edgeContainers = new HashMap<View<CyEdge>, 
AugmentedEdgeContainer>();
+               currentFrame = 0L;
+       }
+
+       /**
+        * Return a set of analyzed edges containing edge coordinates to be 
used for rendering. If an up-to-date
+        * data set is available, the set is returned. Otherwise, calculations 
will be done to re-analyze the current edges.
+        * 
+        * @param networkView The {@link CyNetworkView} containing the edges to 
be analyzed
+        * @param distanceScale The amount of scaling when converting between 
Cytoscape coordinates and OpenGL coordinates
+        * @param currentFrame The current frame of rendering.
+        * @return An up-to-date set of analyzed edge data to be used for 
rendering.
+        */
+       public Collection<AugmentedEdgeContainer> 
getAnalyzedEdges(CyNetworkView networkView, double distanceScale, Long 
currentFrame) {
+               if (this.currentFrame != currentFrame) {
+                       calculateEdgeProperties(networkView, distanceScale);
+                       calculateEdgeCoordinates();
+               }
                
-               // Create the set of containers to be returned
-               Set<EdgeViewContainer> edgeViewContainers = new 
HashSet<EdgeViewContainer>(
-                               networkView.getModel().getEdgeCount());
+               return edgeContainers.values();         
+       }       
+       
+       private void calculateEdgeProperties(CyNetworkView networkView, double 
distanceScale) {
                
                // This map maps each node-pair identifier to the number of 
edges between that pair of nodes
                // The identifier is: max(sourceIndex, targetIndex) * nodeCount 
+ min(sourceIndex, targetIndex)
                Map<Long, Integer> pairCoincidenceCount = new HashMap<Long, 
Integer>(
                                networkView.getModel().getNodeCount());
+               
                long identifier;
                int sourceIndex, targetIndex, edgeNumber;
                int nodeCount = networkView.getModel().getNodeCount();
                CyEdge edge;
                
-               boolean selfEdge;
+               AugmentedEdgeContainer edgeContainer;
                
-               for (View<CyEdge> edgeView : networkView.getEdgeViews()) {      
                
+               for (View<CyEdge> edgeView : networkView.getEdgeViews()) {
+                       edgeContainer = edgeContainers.get(edgeView);
+                       
+                       if (edgeContainer == null) {
+                               edgeContainer = new AugmentedEdgeContainer();
+                               edgeContainer.setEdgeView(edgeView);
+                               edgeContainers.put(edgeView, edgeContainer);
+                       }
+                       
                        edge = edgeView.getModel();
                        
                        // Assign an identifier to each pair of nodes
@@ -65,37 +111,223 @@
                                edgeNumber = 
pairCoincidenceCount.get(identifier) + 1;
                        }
                        
+                       edgeContainer.setPairIdentifier(identifier);
+                       edgeContainer.setEdgeNumber(edgeNumber);
                        pairCoincidenceCount.put(identifier, edgeNumber);
                        
                        // Check if the edge leads from a node to itself
                        if (sourceIndex == targetIndex) {
-                               selfEdge = true;
+                               edgeContainer.setSelfEdge(true);
                        } else {
-                               selfEdge = false;
+                               edgeContainer.setSelfEdge(false);
                        }
                        
-                       EdgeViewContainer container = new EdgeViewContainer();
-                       container.edgeView = edgeView;
-                       container.pairIdentifier = identifier;
-                       container.edgeNumber = edgeNumber;
-                       container.selfEdge = selfEdge;
+                       // Find edge start and end points
+                       
edgeContainer.setStart(obtainCoordinates(edge.getSource(), networkView, 
distanceScale));
+                       
edgeContainer.setEnd(obtainCoordinates(edge.getTarget(), networkView, 
distanceScale));
                        
-                       container.start = obtainCoordinates(edge.getSource(), 
networkView, distanceScale);
-                       container.end = obtainCoordinates(edge.getTarget(), 
networkView, distanceScale);
-                       
-                       edgeViewContainers.add(container);
+                       // Determine if edge has sufficient length to be drawn
+                       if (edgeContainer.getStart() != null && 
edgeContainer.getEnd() != null && 
+                                       
(edgeContainer.getEnd().distance(edgeContainer.getStart()) >= MIN_LENGTH || 
edgeContainer.isSelfEdge())) {
+                               edgeContainer.setSufficientLength(true);
+                       } else {
+                               edgeContainer.setSufficientLength(false);
+                       }
                }
                
                // Update the value for the total number of edges between this 
pair of nodes
-               for (EdgeViewContainer container : edgeViewContainers) {
-                       container.totalCoincidentEdges = 
pairCoincidenceCount.get(container.pairIdentifier);
+               for (AugmentedEdgeContainer container : 
edgeContainers.values()) {
+                       
container.setTotalCoincidentEdges(pairCoincidenceCount.get(container.getPairIdentifier()));
                        
                        // If there was only 1 edge for that pair of nodes, 
make it a straight edge
-                       if (container.totalCoincidentEdges == 1 && 
!container.selfEdge) {
-                               container.straightEdge = true;
+                       if (container.getTotalCoincidentEdges() == 1 && 
!container.isSelfEdge()) {
+                               container.setStraightEdge(true);
                        }
                }
+       }
+       
+       
+       /**
+        * 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.
+        * 
+        * @param edgeContainer The AugmentedEdgeContainer object holding 
additional information about the
+        * edge, including its index amongst the other edges that connect the 
same pair of nodes
+        * @param selfEdge Whether or not the edge leads from a node to itself
+        */
+       private double[] findArcEdgeMetrics(AugmentedEdgeContainer 
edgeContainer) {
                
-               return edgeViewContainers;
+               // 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) 
edgeContainer.getEdgeNumber()));
+               int maxLevel = (int) (Math.sqrt((double) 
edgeContainer.getTotalCoincidentEdges()));
+               
+               int edgesInLevel = edgeLevel * 2 + 1;
+               
+               double curvedEdgeRadius;
+               
+               if (edgeContainer.isSelfEdge()) {
+                       // For self-edges, want greater edge level -> greater 
radius
+                       curvedEdgeRadius = ARC_SELF_EDGE_MINIMUM_RADIUS
+                                       + ARC_SELF_EDGE_RADIUS_FACTOR * 
Math.pow(edgeLevel, ARC_SELF_EDGE_EXPONENTIAL_BASE);
+               } else {
+                       // For regular edges, want greater edge level -> 
smaller radius (more curvature)
+                       curvedEdgeRadius = 
edgeContainer.getStart().distance(edgeContainer.getEnd()) * (0.5 + (double) 3.5 
/ Math.pow(edgeLevel, 2));                   
+               }
+               
+               // The outmost level is usually not completed
+               if (edgeLevel == maxLevel) {
+                       edgesInLevel = (int) 
(edgeContainer.getTotalCoincidentEdges() - Math.pow(maxLevel, 2) + 1);
+               }
+               
+               double edgeRadialAngle = (double) 
(edgeContainer.getEdgeNumber() - Math.pow(edgeLevel, 2)) / edgesInLevel * 
Math.PI * 2;
+
+               // Flip the angle by 180 degrees for every other edge level for 
aesthetic effect
+               if (edgeLevel % 2 == 0) {
+                       edgeRadialAngle = Math.PI - edgeRadialAngle;
+               }
+               
+               return new double[]{curvedEdgeRadius, edgeRadialAngle};
        }
+       
+       /**
+        * Finds the center of a circle passing through 2 points, rotated about 
the displacement axis
+        * by a certain angle.
+        * 
+        * @param edgeContainer The AugmentedEdgeContainer object holding 
additional information about the
+        * edge, including its index amongst the other edges that connect the 
same pair of nodes
+        * @return A position vector representing the center of the circle
+        */
+       private Vector3 findCircleCenter(AugmentedEdgeContainer edgeContainer) {
+               
+               Vector3 start = edgeContainer.getStart();
+               Vector3 end = edgeContainer.getEnd();
+               
+               double[] edgeMetrics = findArcEdgeMetrics(edgeContainer);
+               
+               double radius = edgeMetrics[0];
+               double angle = edgeMetrics[1];
+               
+               if (edgeContainer.isSelfEdge()) {
+                       Vector3 offset = (new Vector3(1, 0, 
0)).multiply(radius).rotate(
+                                       (new Vector3(0, 1, 0)), angle);
+               
+                       return start.plus(offset);
+               } else {
+                       
+                       Vector3 displacement = end.subtract(start);
+                       
+                       double displacementLength = displacement.magnitude();
+                       
+                       // Radius adjustment (can't draw an arc from start to 
end if the radius of the arc is less than half that
+                       // distance)
+                       // radius = Math.max(displacementLength * 2, radius);
+                       
+                       // Use cosine law
+                       double arcAngle;
+                       arcAngle = GeometryToolkit.saferArcCos(
+                                       (2 * radius * radius - 
displacementLength * displacementLength) 
+                                       / (2 * radius * radius));
+                       
+                       double nearCornerAngle = Math.PI / 2 - (arcAngle / 2);
+               
+                       // Set the angle of rotation along the node-to-node 
displacement axis
+                       Vector3 targetDirection = new Vector3(0, 1, 0);
+                       targetDirection = targetDirection.rotate(displacement, 
angle);
+                       
+                       // Offset vector that points from first node to the 
circle's center
+                       Vector3 circleCenterOffset = 
displacement.rotate(targetDirection.cross(displacement), nearCornerAngle);
+                       circleCenterOffset.normalizeLocal();
+                       circleCenterOffset.multiplyLocal(radius);
+               
+                       return start.plus(circleCenterOffset);
+               }
+       }
+       
+       private void calculateEdgeCoordinates() {
+               
+               boolean selfEdge;
+               Vector3 start, end;
+               
+               for (AugmentedEdgeContainer edgeContainer : 
edgeContainers.values()) {
+                       
+                       // Only perform coordinate calculations if the edge has 
sufficient length
+                       if (edgeContainer.isSufficientLength()) {
+                               
+                               View<CyEdge> edgeView = 
edgeContainer.getEdgeView();
+                               
+                               start = edgeContainer.getStart();
+                               end = edgeContainer.getEnd();
+                               selfEdge = edgeContainer.isSelfEdge();
+                               
+                               // General points along the arc
+                               Vector3[] points;
+                               
+                               if (edgeContainer.isStraightEdge()) {
+                                       
+                                       // Draw the correct type of edge 
depending on the visual property
+                                       if 
(edgeView.getVisualProperty(BasicVisualLexicon.EDGE_LINE_TYPE)
+                                                       == 
LineTypeVisualProperty.EQUAL_DASH) {
+                                               points = 
EdgeCoordinateCalculator.generateStraightEdgeSparseCoordinates(
+                                                               start, end, 
DASHED_EDGE_SPACING);
+                                       
+                                       } else if 
(edgeView.getVisualProperty(BasicVisualLexicon.EDGE_LINE_TYPE)
+                                                       == 
LineTypeVisualProperty.DOT) {
+                                               points = 
EdgeCoordinateCalculator.generateStraightEdgeSparseCoordinates(
+                                                               start, end, 
DOTTED_EDGE_SPACING);
+                               
+                                               
+                                       // Draw regular edges for the catch-all 
case
+                                       } else {
+                                               points = 
EdgeCoordinateCalculator.generateStraightEdgeCoordinates(
+                                                               start, end, 1);
+                                               
+                                       }
+                                       
+                               } else {
+                                       // Find the arc circle's center
+                                       Vector3 circleCenter = 
findCircleCenter(edgeContainer);
+                                       
+                                       // Draw the correct type of edge 
depending on the visual property
+                                       if 
(edgeView.getVisualProperty(BasicVisualLexicon.EDGE_LINE_TYPE)
+                                                       == 
LineTypeVisualProperty.EQUAL_DASH) {
+                                               points = 
EdgeCoordinateCalculator.generateArcEdgeSparseCoordinates(
+                                                               start, end, 
circleCenter, DASHED_EDGE_SPACING, selfEdge);
+                                       
+                                       } else if 
(edgeView.getVisualProperty(BasicVisualLexicon.EDGE_LINE_TYPE)
+                                                       == 
LineTypeVisualProperty.DOT) {
+                                               points = 
EdgeCoordinateCalculator.generateArcEdgeSparseCoordinates(
+                                                               start, end, 
circleCenter, DOTTED_EDGE_SPACING, selfEdge);
+                                       
+                                               
+                                       // Draw regular edges for the catch-all 
case
+                                       } else {
+                                               points = 
EdgeCoordinateCalculator.generateArcEdgeCoordinates(
+                                                               start, end, 
circleCenter, NUM_SEGMENTS, selfEdge);
+                                               
+                                       }
+                               }
+                               
+                               edgeContainer.setCoordinates(points);
+                       }
+               }
+       }
+       
+       // Obtain the coordinates of a given node, eg. source or target node
+       // Returns null if failed to find coordinates
+       private Vector3 obtainCoordinates(CyNode node, CyNetworkView 
networkView, double distanceScale) {
+               Vector3 coordinates = null;
+               
+               View<CyNode> nodeView = networkView.getNodeView(node);
+               
+               if (nodeView != null) {
+                       double x = 
nodeView.getVisualProperty(BasicVisualLexicon.NODE_X_LOCATION) / distanceScale;
+                       double y = 
nodeView.getVisualProperty(BasicVisualLexicon.NODE_Y_LOCATION) / distanceScale;
+                       double z = 
nodeView.getVisualProperty(BasicVisualLexicon.NODE_Z_LOCATION) / distanceScale;
+                       
+                       // TODO: Perform a check to ensure none of x, y, z are 
null?
+                       coordinates = new Vector3(x, y, z);
+               }
+               
+               return coordinates;
+       }
 }

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