Author: apico
Date: 2009-03-12 15:32:22 -0700 (Thu, 12 Mar 2009)
New Revision: 16251

Added:
   
csplugins/trunk/ucsf/apico/CellularLayout/src/org/genmapp/golayout/UnCrossAction.java
Log:


Added: 
csplugins/trunk/ucsf/apico/CellularLayout/src/org/genmapp/golayout/UnCrossAction.java
===================================================================
--- 
csplugins/trunk/ucsf/apico/CellularLayout/src/org/genmapp/golayout/UnCrossAction.java
       2009-03-12 22:31:01 UTC (rev 16250)
+++ 
csplugins/trunk/ucsf/apico/CellularLayout/src/org/genmapp/golayout/UnCrossAction.java
       2009-03-12 22:32:22 UTC (rev 16251)
@@ -0,0 +1,662 @@
+package org.genmapp.golayout;
+
+import giny.model.Edge;
+import giny.model.Node;
+import giny.view.NodeView;
+
+import java.awt.event.ActionEvent;
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import javax.swing.JOptionPane;
+import javax.swing.undo.AbstractUndoableEdit;
+
+import cytoscape.CyNetwork;
+import cytoscape.Cytoscape;
+import cytoscape.task.Task;
+import cytoscape.task.TaskMonitor;
+import cytoscape.task.ui.JTaskConfig;
+import cytoscape.task.util.TaskManager;
+import cytoscape.util.CytoscapeAction;
+import cytoscape.util.undo.CyUndo;
+import cytoscape.view.CyNetworkView;
+
+/**
+ * Rearranges the set of selected nodes in a way that minimizes edge crossings.
+ * Uses a very simple method. 1. randomly order the list of nodes 2. select the
+ * first node in the list. 3. for each subsequent node in the list, calculate
+ * number of edge crossings before and after a possible permutation, as follows
+ * a. for each edge incident to each node, (1) for each edge in the network, 
see
+ * if the two edges intersect b. total the intersections for all incident 
edges,
+ * before and after permutation 4. permute the node with the node in the list
+ * that has resulted in largest drop between before and after calculation of
+ * edge crossings. 5. increment iterations counter. 6. if iterations counter >
+ * threshold, then done. repeat at 3 using the node that has just been switched
+ * into first position
+ * 
+ * @author Allan Kuchinsky, Agilent Technologies
+ * 
+ */
+
+...@suppresswarnings("serial")
+class UnCrossAction extends CytoscapeAction {
+
+       private static List _selectedNodeViews;
+
+       /**
+        * Array of NodeViews
+        */
+       private static NodeView[] _nodeViews;
+
+       /**
+        * Data structures containing totals for before/after edge crossings 
should
+        * be same size and parallel to the NodeViews array
+        */
+
+       /**
+        * edge crossings for all nodes before swap
+        */
+       private static int[] _beforeEdgeCrossings;
+
+       /**
+        * edge crossings for all nodes after swap
+        */
+       private static int[] _afterEdgeCrossings;
+
+       /**
+        * totals of edge crossings after shift
+        */
+       private static int[] _totalEdgeCrossings;
+
+       /**
+        * for undo/redo of uncrossing
+        */
+       private static Point2D[] _undoOffsets;
+
+       private static Point2D[] _redoOffsets;
+
+       /**
+        * number of edge crossings for best fit so far
+        */
+       private static int _nbr_crossings_for_best_fit;
+
+       private static int _index_for_best_fit;
+
+       /**
+        * before and after positions for candidate and other node
+        */
+       static Point2D _candidate_before_locn = null;
+
+       static Point2D _candidate_after_locn = null;
+
+       static Point2D _other_before_locn = null;
+
+       static Point2D _other_after_locn = null;
+
+       static Point2D _tmpPoint;
+
+       /**
+        * Cytoscape working objects
+        */
+       private static CyNetwork _net;
+
+       private static CyNetworkView _view;
+
+       /**
+        * number of iterations before quitting
+        */
+       // for debugging set to 1
+       private static final int ITERATION_LIMIT = 10;
+
+       /**
+        * for performance reasons, set threshold so that this doesn't execute 
with
+        * large networks
+        */
+       public static final int UNCROSS_THRESHOLD = 100;
+
+       /**
+        * iterations counter
+        */
+       private static int iteration = 0;
+
+       /**
+        * Constructor.
+        * 
+        * Note: Alt-U is used by default on MacOS; use Alt-X instead
+        * setAcceleratorCombo(java.awt.event.KeyEvent.VK_X, 
ActionEvent.ALT_MASK);
+        */
+       public UnCrossAction() {
+       }
+
+       public void actionPerformed(ActionEvent e) {
+               // required inherited method
+       }
+
+       /**
+        * Rearranges the set of selected nodes in a way that minimizes edge
+        * crossings. Uses a very simple method. 1. randomly order the list of 
nodes
+        * 2. select the first node in the list. 3. for each subsequent node in 
the
+        * list, calculate number of edge crossings before and after a possible
+        * permutation, as follows a. for each edge incident to each node, (1) 
for
+        * each edge in the network, see if the two edges intersect b. total the
+        * intersections for all incident edges, before and after permutation 4.
+        * permute the node with the node in the list that has resulted in 
largest
+        * drop between before and after calculation of edge crossings. 5. 
increment
+        * iterations counter. 6. if iterations counter > threshold, then done.
+        * repeat at 3 using the node that has just been switched into first
+        * position *
+        * 
+        * @param nodeViews
+        * @param calledByEndUser
+        *            was this called from the UI or by another part of 
BubbleRouter
+        *            (avoids undo/redo logic if called by another component)
+        */
+       public static void unCross(List<NodeView> nodeViews, boolean 
calledByEndUser) {
+
+               // warn if no nodeViewss are selected
+               if (nodeViews.size() <= 0) {
+//                     JOptionPane
+//                                     
.showMessageDialog(Cytoscape.getDesktop(),
+//                                                     "You must first select 
some nodes in order to minimize edge crossings.");
+                       return;
+
+               } else if (Cytoscape.getCurrentNetwork().getNodeCount() > 
UNCROSS_THRESHOLD) {
+//                     if (calledByEndUser) {
+//                             JOptionPane
+//                                             .showMessageDialog(
+//                                                             
Cytoscape.getDesktop(),
+//                                                             "Sorry, this 
network is too large to run incremental edge cross miminization."
+//                                                                             
+ "\nYou should one of the automated layout tools instead.");
+//                     }
+                       return;
+               }
+
+               else {
+                       UnCrossAction uncross = new UnCrossAction();
+
+                       Task unCrossTask = uncross.new UnCrossTask(nodeViews,
+                                       calledByEndUser);
+
+                       JTaskConfig jTaskConfig = new JTaskConfig();
+                       jTaskConfig.setOwner(Cytoscape.getDesktop());
+                       jTaskConfig.displayCloseButton(true);
+                       jTaskConfig.displayStatus(true);
+                       jTaskConfig.displayTimeElapsed(true);
+
+                       jTaskConfig.setAutoDispose(true);
+
+                       // Execute Task in New Thread; pops open JTask Dialog 
Box.
+                       TaskManager.executeTask(unCrossTask, jTaskConfig);
+               }
+
+       }
+
+       public class UnCrossTask implements Task {
+               boolean interrupted = false;
+
+               private TaskMonitor taskMonitor = null;
+
+               private List<NodeView> nodes;
+
+               // only implement undo/redo logic if we are called by the end 
user,
+               // rather than by another class
+               private boolean _calledByEndUser = true;
+
+               /**
+                * 
+                * @param nodeViews
+                * @param needUndo
+                */
+               public UnCrossTask(List<NodeView> nodeViews, boolean needUndo) {
+                       _calledByEndUser = needUndo;
+
+                       nodes = nodeViews;
+               }
+
+               @SuppressWarnings("serial")
+               public void run() {
+                       if (taskMonitor == null) {
+                               throw new IllegalStateException("Task Monitor 
is not set");
+                       }
+
+                       initialize(nodes);
+
+                       for (int j = 0; j < _nodeViews.length; j++) {
+                               _undoOffsets[j] = _nodeViews[j].getOffset();
+                       }
+
+                       for (iteration = 0; iteration < ITERATION_LIMIT; 
iteration++) {
+                               taskMonitor.setStatus("Iteration " + iteration 
+ " of "
+                                               + ITERATION_LIMIT);
+                               taskMonitor
+                                               .setPercentCompleted((int) (100 
* (1.0 * iteration) / ITERATION_LIMIT));
+
+                               for (int i = 0; i < _nodeViews.length; i++) {
+                                       if (taskMonitor != null) {
+                                               if ((i % 5) == 0) {
+                                                       taskMonitor
+                                                                       
.setPercentCompleted((int) (100 * (1.0 * iteration) / ITERATION_LIMIT)
+                                                                               
        + ((int) ((i * 1.0 / _nodeViews.length) * (100.0 / ITERATION_LIMIT))));
+                                                       
taskMonitor.setStatus("Iteration " + iteration
+                                                                       + " of 
" + ITERATION_LIMIT + ", Node " + i
+                                                                       + " of 
" + _nodeViews.length);
+                                               }
+                                       }
+                                       permuteToBestFit(i);
+                               }
+
+                               // performance tuning
+                               if (nodes.size() <= UNCROSS_THRESHOLD) {
+                                       _view.redrawGraph(true, true);
+                               }
+
+                       }
+                       for (int m = 0; m < _nodeViews.length; m++) {
+                               _redoOffsets[m] = _nodeViews[m].getOffset();
+                       }
+
+                       // only implement undo/redo logic if we are called by 
the end user,
+                       // rather than by another class
+                       if (_calledByEndUser) {
+                               CyUndo.getUndoableEditSupport().postEdit(
+                                               new AbstractUndoableEdit() {
+
+                                                       public String 
getPresentationName() {
+                                                               return 
"UnCross";
+                                                       }
+
+                                                       public String 
getRedoPresentationName() {
+
+                                                               return "Redo: 
Edge Minimization";
+                                                       }
+
+                                                       public String 
getUndoPresentationName() {
+                                                               return "Undo: 
Edge Minimization";
+                                                       }
+
+                                                       public void redo() {
+                                                               for (int m = 0; 
m < _nodeViews.length; m++) {
+                                                                       
_nodeViews[m].setOffset(_redoOffsets[m]
+                                                                               
        .getX(), _redoOffsets[m].getY());
+                                                               }
+                                                       }
+
+                                                       public void undo() {
+                                                               for (int m = 0; 
m < _nodeViews.length; m++) {
+                                                                       
_nodeViews[m].setOffset(_undoOffsets[m]
+                                                                               
        .getX(), _undoOffsets[m].getY());
+                                                               }
+                                                       }
+                                               });
+                       }
+
+                       // redraw one last time, just in case we haven't
+                       // been incrementally redrawing
+                       _view.redrawGraph(true, true);
+               }
+
+               /**
+                * initialize internal data structures for algorithm and first 
iteration
+                * 
+                * @param nodeViews
+                */
+               private void initialize(List<NodeView> nodeViews) {
+
+                       // initialize the data arrays
+                       _beforeEdgeCrossings = new int[nodeViews.size()];
+                       _afterEdgeCrossings = new int[nodeViews.size()];
+                       _totalEdgeCrossings = new int[nodeViews.size()];
+                       _undoOffsets = new Point2D[nodeViews.size()];
+                       _redoOffsets = new Point2D[nodeViews.size()];
+
+                       // start the first iteration
+                       _selectedNodeViews = nodeViews;
+                       iteration = 0;
+                       _net = Cytoscape.getCurrentNetwork();
+                       _view = Cytoscape.getCurrentNetworkView();
+
+                       // first initialize array of NodeViews, starting from 
randomly
+                       // generated position
+                       int len = nodeViews.size();
+                       int startPosn = (int) (len * Math.random());
+                       _nodeViews = new NodeView[len];
+                       ListIterator it = 
_selectedNodeViews.listIterator(startPosn);
+                       int i = 0;
+                       while (it.hasNext()) {
+                               _nodeViews[i] = (NodeView) it.next();
+                               i++;
+                       }
+                       it = _selectedNodeViews.listIterator(startPosn);
+                       while (it.hasPrevious()) {
+                               _nodeViews[i] = (NodeView) it.previous();
+                               i++;
+                       }
+
+                       // now calculate base set of edge crossing values
+                       for (int j = 0; j < _nodeViews.length; j++) {
+                               NodeView nv = _nodeViews[j];
+                               Point2D locn = nv.getOffset();
+                               _beforeEdgeCrossings[j] = 
calculateEdgeCrossings(nv, locn,
+                                               null, null);
+                       }
+
+                       int totalCrossings = 
calculateTotalCrossings(_beforeEdgeCrossings);
+                       // we start at the first index
+                       _totalEdgeCrossings[0] = totalCrossings;
+               }
+
+               /**
+                * 
+                * @param edgeCrossings
+                * @return
+                */
+               private int calculateTotalCrossings(int[] edgeCrossings) {
+                       int total = 0;
+                       for (int i = 0; i < edgeCrossings.length; i++) {
+                               total += edgeCrossings[i];
+                       }
+                       return total;
+               }
+
+               /**
+                * permute node at index with one of the other nodes such that 
edge
+                * crossings are minimized
+                * 
+                * @param index
+                *            integer index of node to be permuted
+                * 
+                */
+               private void permuteToBestFit(int index) {
+                       /**
+                        * loop through _nodeViews and for each _nodeView a. 
calculate edge
+                        * crossings for the node at index if moved to position 
of _nodeView
+                        * b. calculate edge crossings for the node at current 
position if
+                        * moved to position of _nodeView at index c. sum a and 
b and store
+                        * in total edge crossings for counter position d. 
permute _nodeView
+                        * at index with _nodeView at position
+                        * 
+                        */
+
+                       for (int i = 0; i < _beforeEdgeCrossings.length; i++) {
+                               NodeView nv = _nodeViews[index];
+                               if (i != index) // don't permute a node with 
itself
+                               {
+                                       _candidate_before_locn = nv.getOffset();
+                                       NodeView otherNv = _nodeViews[i];
+                                       _other_before_locn = 
otherNv.getOffset();
+
+                                       // now permute
+                                       nv.setOffset(_other_before_locn.getX(), 
_other_before_locn
+                                                       .getY());
+                                       
otherNv.setOffset(_candidate_before_locn.getX(),
+                                                       
_candidate_before_locn.getY());
+
+                                       // now calculate edge crossings after 
permuting
+                                       for (int j = 0; j < _nodeViews.length; 
j++) {
+                                               NodeView newNv = _nodeViews[j];
+                                               Point2D locn = 
newNv.getOffset();
+
+                                               // efficiency hack: use cached 
values for all but
+                                               // but the candidate and 
swapped nodeViews
+                                               if ((j == index) || (j == i)) {
+                                                       _afterEdgeCrossings[j] 
= calculateEdgeCrossings(
+                                                                       newNv, 
locn, null, null);
+                                               } else {
+                                                       _afterEdgeCrossings[j] 
= _beforeEdgeCrossings[j];
+                                               }
+
+                                       }
+                                       int totalCrossings = 
calculateTotalCrossings(_afterEdgeCrossings);
+                                       _totalEdgeCrossings[i] = totalCrossings;
+
+                                       // now shift back before going on to 
next potential swap
+                                       
nv.setOffset(_candidate_before_locn.getX(),
+                                                       
_candidate_before_locn.getY());
+                                       
otherNv.setOffset(_other_before_locn.getX(),
+                                                       
_other_before_locn.getY());
+
+                               }
+                       }
+                       // now go through the totals and find index with 
minimum edge
+                       // crossings
+                       _index_for_best_fit = index;
+                       _nbr_crossings_for_best_fit = 
_totalEdgeCrossings[index];
+                       for (int k = 0; k < _nodeViews.length; k++) {
+                               if (_totalEdgeCrossings[k] < 
_nbr_crossings_for_best_fit) {
+                                       _index_for_best_fit = k;
+                                       _nbr_crossings_for_best_fit = 
_totalEdgeCrossings[k];
+                               }
+                       }
+                       if (_index_for_best_fit != index) // swap for lower 
edge crossings
+                       {
+                               // shift X position by a small random amount so 
as to indicate
+                               // movement
+                               NodeView swapNv = 
_nodeViews[_index_for_best_fit];
+                               _tmpPoint = swapNv.getOffset();
+                               
swapNv.setOffset(_nodeViews[index].getXPosition(),
+                                               
_nodeViews[index].getYPosition());
+                               _nodeViews[index].setOffset(_tmpPoint.getX(), 
_tmpPoint.getY());
+                               _totalEdgeCrossings[_index_for_best_fit] = 
_totalEdgeCrossings[index];
+                               _totalEdgeCrossings[index] = 
_nbr_crossings_for_best_fit;
+                       }
+
+                       // now need to recalculate baseline edge crossing 
counts after swap
+                       for (int j = 0; j < _nodeViews.length; j++) {
+                               NodeView nv = _nodeViews[j];
+                               Point2D locn = nv.getOffset();
+                               _beforeEdgeCrossings[j] = 
calculateEdgeCrossings(nv, locn,
+                                               null, null);
+                       }
+               }
+
+               /**
+                * calculate edge crossings for edges incident to NodeView at 
index when
+                * node is at position specified by locn
+                * 
+                * @param nv
+                *            the NodeView for which we are calculating edge 
crossings
+                * @param nvLocn
+                *            the position for NodeView (may be actual or 
tentative)
+                * @param swapNV
+                *            the NodeView that we are tentatively swapping 
locations
+                *            with (may be null)
+                * @param swapLocn
+                *            position for NodeView that we are tentatively 
swapping
+                *            locations with (may be null)
+                * @return number of edgeCrossings
+                */
+               private int calculateEdgeCrossings(NodeView nv, Point2D nvLocn,
+                               NodeView swapNv, Point2D swapLocn) {
+                       int crossings = 0;
+                       Node n = nv.getNode();
+                       List myEdges = _net.getAdjacentEdgesList(n, true, true, 
true);
+                       for (int i = 0; i < myEdges.size(); i++) {
+                               crossings += calculateCrossingsForEdge((Edge) 
myEdges.get(i),
+                                               nv, nvLocn, swapNv, swapLocn);
+                       }
+
+                       return crossings;
+               }
+
+               /**
+                * calculate edge crossings for a specific edge
+                * 
+                * @param edge
+                * @param nv
+                *            the NodeView for which we are calculating edge 
crossings
+                * @param nvLocn
+                *            the position for NodeView (may be actual or 
tentative)
+                * @param swapNV
+                *            the NodeView that we are tentatively swapping 
locations
+                *            with (may be null)
+                * @param swapLocn
+                *            position for NodeView that we are tentatively 
swapping
+                *            locations with (may be null)
+                * @return number of edges the input edge intersects
+                */
+               private int calculateCrossingsForEdge(Edge edge, NodeView nv,
+                               Point2D nvLocn, NodeView swapNv, Point2D 
swapLocn) {
+                       int crossings = 0;
+                       Iterator allEdges = _net.edgesIterator();
+                       while (allEdges.hasNext()) {
+                               Edge otherEdge = (Edge) allEdges.next();
+                               if (edge != otherEdge) {
+                                       crossings += edgeCrossings(edge, 
otherEdge, nv, nvLocn,
+                                                       swapNv, swapLocn);
+                               }
+                       }
+                       return crossings;
+               }
+
+               /**
+                * calculate edge crossings for the two input edges (should be 
0 or 1)
+                * 
+                * @param thisEdge
+                *            first edge
+                * @param otherEdge
+                *            secondEdge
+                * @param nv
+                *            the NodeView for which we are calculating edge 
crossings
+                * @param nvLocn
+                *            the position for NodeView (may be actual or 
tentative)
+                * @param swapNV
+                *            the NodeView that we are tentatively swapping 
locations
+                *            with (may be null)
+                * @param swapLocn
+                *            position for NodeView that we are tentatively 
swapping
+                *            locations with (may be null)
+                * @return number of edge crossings for the two edges (should 
be 0 or 1
+                */
+               private int edgeCrossings(Edge thisEdge, Edge otherEdge, 
NodeView nv,
+                               Point2D nvLocn, NodeView swapNv, Point2D 
swapLocn) {
+
+                       NodeView thisEdgeSourceView = _view.getNodeView(thisEdge
+                                       .getSource());
+                       NodeView thisEdgeTargetView = _view.getNodeView(thisEdge
+                                       .getTarget());
+                       NodeView otherEdgeSourceView = 
_view.getNodeView(otherEdge
+                                       .getSource());
+                       NodeView otherEdgeTargetView = 
_view.getNodeView(otherEdge
+                                       .getTarget());
+
+                       Line2D thisLine = new 
Line2D.Double(thisEdgeSourceView.getOffset(),
+                                       thisEdgeTargetView.getOffset());
+                       Line2D otherLine = new Line2D.Double(otherEdgeSourceView
+                                       .getOffset(), 
otherEdgeTargetView.getOffset());
+
+                       // find the intersection point between the two lines
+                       Point2D intersectionPt = getIntersectionPoint(thisLine, 
otherLine);
+
+                       if (intersectionPt == null) {
+                               return 0; // no intersection
+                       }
+
+                       // eliminate the case where lines intersect at their 
end points,
+                       // i.e. it's a junction at a node, not an edge crossing
+                       else if ((((int) (intersectionPt.getX()) == ((int) 
thisLine.getX1())) && ((int) (intersectionPt
+                                       .getY()) == ((int) thisLine.getY1())))
+                                       || (((int) (intersectionPt.getX()) == 
((int) thisLine
+                                                       .getX2())) && ((int) 
(intersectionPt.getY()) == ((int) thisLine
+                                                       .getY2()))))
+                       // we are at the endpoint of one line. Are we at 
endPoint of the
+                       // other?
+                       {
+                               if ((((int) (intersectionPt.getX()) == ((int) 
otherLine.getX1())) && ((int) (intersectionPt
+                                               .getY()) == ((int) 
otherLine.getY1())))
+                                               || (((int) 
(intersectionPt.getX()) == ((int) otherLine
+                                                               .getX2())) && 
((int) (intersectionPt.getY()) == ((int) otherLine
+                                                               .getY2())))) {
+                                       return 0;
+                               }
+                       }
+
+                       // make sure that point is actually on both lines, not 
their
+                       // extensions to infinity
+                       else if (thisLine.intersectsLine(otherLine)) {
+                               return 1;
+
+                       }
+
+                       // not an edge crossing if we get to this point
+                       return 0;
+
+               }
+
+               /**
+                * 
+                * @param thisLine
+                * @param otherLine
+                * @return point where the two lines intersect
+                */
+               private Point2D getIntersectionPoint(Line2D thisLine, Line2D 
otherLine) {
+                       double e1x1 = thisLine.getX1();
+                       double e1x2 = thisLine.getX2();
+                       double e1y1 = thisLine.getY1();
+                       double e1y2 = thisLine.getY2();
+
+                       double e2x1 = otherLine.getX1();
+                       double e2x2 = otherLine.getX2();
+                       double e2y1 = otherLine.getY1();
+                       double e2y2 = otherLine.getY2();
+
+                       double dx1 = e1x2 - e1x1;
+                       double dx2 = e2x2 - e2x1;
+
+                       // both lines are vertical and parallel (unless the 
same)
+                       if ((dx1 == 0.0d) && (dx2 == 0.0d)) {
+                               return null; 
+                       }
+
+                       double m1 = Double.NaN;
+                       double m2 = Double.NaN;
+
+                       if (dx1 != 0.0) {
+                               m1 = (e1y2 - e1y1) / dx1;
+                       }
+
+                       if (dx2 != 0.0) {
+                               m2 = (e2y2 - e2y1) / dx2;
+                       }
+
+                       if (((int) dx1) == 0) // first line is vertical
+                       {
+                               return new Point2D.Double(e1x1, m2 * (e1x1 - 
e2x2) + e2y2);
+                       }
+                       if (((int) dx2) == 0) // second line is vertical
+                       {
+                               return new Point2D.Double(e2x2, m1 * (e2x2 - 
e1x1) + e1y1);
+                       }
+
+                       Double xInt = (-m2 * e2x2 + e2y2 + m1 * e1x1 - e1y1) / 
(m1 - m2);
+                       Double yInt = m1 * (xInt - e1x1) + e1y1;
+
+                       return new Point2D.Double(xInt, yInt);
+               }
+
+               public void setTaskMonitor(TaskMonitor taskMonitor) {
+                       if (this.taskMonitor != null) {
+                               throw new IllegalStateException("Task Monitor 
is already set");
+                       }
+
+                       this.taskMonitor = taskMonitor;
+               }
+
+               /**
+                * 
+                * Sets the task title
+                * 
+                * @return human readable task title
+                */
+               public String getTitle() {
+                       return new String("Minimize Edge Crossings");
+               }
+
+               public void halt() {
+                       this.interrupted = true;
+               }
+       }
+
+}


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