www.pudn.com > 19832002.rar > GraphAlgorithm.java


import java.awt.*; 
/** 
  * GraphAlgorithm.java 
  * Dijkstra×î¶Ì·¾¶Ëã·¨ÑÝʾ 
  * @author: Carla Laffra 
  * @date:   March, 1996 
  * 
  * Applet for explaining graph algorithms.  
  * It currently only explains Dijkstra's shortest path algorithm,  
  * but it can easily be extended to handle more algorithms. 
  * 
  * Copyright 1996, Carla Laffra, all rights reserved 
  */ 
 
 
public class GraphAlgorithm extends java.applet.Applet { 
 
    GraphCanvas graphcanvas = new GraphCanvas(this); 
    Options options = new Options(this);    
    Documentation documentation = new Documentation(); 
 
    public void init() { 
	setLayout(new BorderLayout(10, 10)); 
	add("Center", graphcanvas); 
	add("North", documentation); 
	add("East", options); 
    } 
 
    public Insets insets() { 
	return new Insets(10, 10, 10, 10); 
    } 
 
    public void lock() { 
	graphcanvas.lock(); 
	options.lock(); 
    }  
 
    public void unlock() { 
	graphcanvas.unlock(); 
	options.unlock(); 
    }  
} 
 
 
class Options extends Panel { 
// set of options at the right of the screen 
    Button b1 = new Button("clear"); 
    Button b2 = new Button("run"); 
    Button b3 = new Button("step"); 
    Button b4 = new Button("reset"); 
    Button b5 = new Button("example"); 
    Button b6 = new Button("exit"); 
    GraphAlgorithm parent;    
    boolean Locked=false; 
   
    Options(GraphAlgorithm myparent) { 
	parent = myparent; 
	setLayout(new GridLayout(6, 1, 0, 10)); 
	add(b1); 
	add(b2); 
	add(b3); 
	add(b4); 
	add(b5);  
	add(b6); 
    } 
 
    public boolean action(Event evt, Object arg) { 
	if (evt.target instanceof Button) { 
	  if (((String)arg).equals("step")) { 
	     if (!Locked) { 
	        b3.setLabel("next step"); 
	        parent.graphcanvas.stepalg(); 
	     } 
	     else parent.documentation.doctext.showline("locked"); 
	  } 
	  if (((String)arg).equals("next step"))  
	     parent.graphcanvas.nextstep(); 
	  if (((String)arg).equals("reset")) {  
	     parent.graphcanvas.reset(); 
	     b3.setLabel("step"); 
	     parent.documentation.doctext.showline("all items"); 
	  } 
	  if (((String)arg).equals("clear")) {  
	     parent.graphcanvas.clear(); 
	     b3.setLabel("step"); 
	     parent.documentation.doctext.showline("all items"); 
	  } 
	  if (((String)arg).equals("run")) { 
	     if (!Locked)   
	        parent.graphcanvas.runalg(); 
	     else parent.documentation.doctext.showline("locked"); 
	  } 
	  if (((String)arg).equals("example")) { 
	     if (!Locked)    
	        parent.graphcanvas.showexample(); 
	     else parent.documentation.doctext.showline("locked"); 
	  }  
	  if (((String)arg).equals("exit")) {  
	     System.exit(0); 
	  }  
	}                    
	return true;  
    } 
     
    public void lock() { 
	Locked=true; 
    } 
 
    public void unlock() { 
	Locked=false; 
	b3.setLabel("step");  
    }  
}     
 
 
class Documentation extends Panel { 
// Documentation on top of the screen 
    DocOptions docopt = new DocOptions(this); 
    DocText doctext = new DocText(); 
 
    Documentation() { 
	setLayout(new BorderLayout(10, 10)); 
	add("West", docopt); 
	add("Center", doctext); 
    } 
} 
 
 
class DocOptions extends Panel { 
    Choice doc = new Choice(); 
    Documentation parent;    
    
    DocOptions(Documentation myparent) { 
	setLayout(new GridLayout(2, 1, 5, 0)); 
	parent = myparent; 
	add(new Label("DOCUMENTATION:")); 
	doc.addItem("draw nodes"); 
	doc.addItem("remove nodes");  
	doc.addItem("move nodes"); 
	doc.addItem("the startnode"); 
	doc.addItem("draw arrows");  
	doc.addItem("change weights"); 
	doc.addItem("remove arrows"); 
	doc.addItem("clear / reset");  
	doc.addItem("run algorithm"); 
	doc.addItem("step through"); 
	doc.addItem("example");  
	doc.addItem("exit");  
	doc.addItem("all items"); 
	add(doc); 
    } 
   
    public boolean action(Event evt, Object arg) { 
        if (evt.target instanceof Choice) { 
	    String str=new String(doc.getSelectedItem()); 
	    parent.doctext.showline(str);   
        }              
       	return true; 
    } 
} 
 
 
class DocText extends TextArea { 
    final String drawnodes = new String("DRAWING NODES:\n"+ 
	  "Draw a node by clicking the mouse.\n\n"); 
    final String rmvnodes = new String("REMOVE NODES:\n"+ 
	  "To remove a node press  and click on the node.\n"+ 
	  "You can not remove the startnode.\n"+ 
	  "Select another startnode, then you can remove the node.\n\n"); 
    final String mvnodes = new String("MOVING NODES\n"+ 
	  "To move a node press , click on the node,\nand drag it to"+ 
	  " its new position.\n\n"); 
    final String startnode = new String("STARTNODE:\n"+ 
	  "The startnode is blue, other nodes are grey.\n"+  
	  "The first node you draw on the screen will be the startnode.\n"+ 
	  "To select another startnode press , click on the startnode,\n"+ 
	  "and drag the mouse to another node.\n"+ 
	  "To delete the startnode, first select another startnode, and then"+ 
	  "\nremove the node the usual way.\n\n");  
    final String drawarrows = new String("DRAWING ARROWS:\n"+ 
	  "To draw an arrow click mouse in a node,"+ 
	  "and drag it to another node.\n\n"); 
    final String weight = new String("CHANGING WEIGHTS:\n"+ 
	  "To change the weight of an arrow, click on the arrowhead and drag\n"+ 
	  "it along the arrow.\n\n"); 
    final String rmvarrows = new String("REMOVE ARROWS:\n"+ 
	  "To remove an arrow, change its weight to 0.\n\n"); 
    final String clrreset = new String(" BUTTON: "+ 
	  "Remove the current graph from the screen.\n"+ 
	  " BUTTON: "+ 
	  "Remove the results of the algorithm from the graph,\n"+ 
	  " and unlock screen.\n\n"); 
    final String runalg = new String(" BUTTON: "+ 
	  "Run the algorithm on the graph, there will be a time\n" + 
	  "delay of +/- 1 second between steps.\n"+ 
	  "To run the algorithm slower, use .\n"); 
    final String step = new String(" BUTTON: " + 
	  "An opportunity to step through the algorithm.\n"); 
    final String example = new String(" BUTTON: "+ 
	  "Displays a graph on the screen for you.\n"+ 
	  "You can then use  or \n"); 
    final String exitbutton = new String(" BUTTON: " + 
	  "Only works if applet is run with appletviewer.\n"); 
    final String toclose = new String("ERROR: "+ 
	  "This position is to close to another node/arrow.\n"); 
    final String done = new String("Algorithm has finished, " + 
	  "follow orange arrows from startnode to any node "+ 
	  "to get\nthe shortest path to " + 
	  "the node. The length of the path is written in the node.\n" + 
          "press  to reset the graph, and unlock the screen."); 
    final String some = new String("Algorithm has finished, " + 
	  "follow orange arrows from startnode to any node "+ 
	  "to get\nthe shortest path to " + 
	  "the node. The length of the path is written in the node.\n" + 
          "There are no paths from the startnode to any gray node.\n" + 
          "press  to reset the graph, and unlock the screen."); 
    final String none = new String("Algorithm has finished, " + 
	  "there are no nodes reachable from the start node.\n"+ 
	  "press  to reset the graph, and unlock the screen."); 
   
    final String maxnodes = new String("ERROR: "+  
	  "Maximum number of nodes reached!\n\n"); 
    final String info = new String("DOCUMENTATION:\n"+ 
	  "You can scroll through the documentation or get documentation\n"+ 
	  "on a specific "+ 
	  "item by selecting the item on the left.\nSelecting  "+ 
	  "brings you back "+ 
	  " to the scrolling text.\n\n");  
    final String locked = new String("ERROR: "+ 
	  "Keyboard/mouse locked for this action.\n"+ 
	  "Either press  or .\n");  
 
    final String doc = info + drawnodes + rmvnodes + mvnodes +  
		       startnode + drawarrows + weight + rmvarrows +  
		       clrreset + runalg + step + example + exitbutton; 
 
    DocText() { 
	super(5, 2); 
	setText(doc); 
    } 
     
    public void showline(String str) { 
	if (str.equals("draw nodes"))              setText(drawnodes); 
	else if (str.equals("remove nodes"))       setText(rmvnodes); 
	else if (str.equals("move nodes"))         setText(mvnodes); 
	else if (str.equals("the startnode"))      setText(startnode); 
	else if (str.equals("draw arrows"))        setText(drawarrows); 
	else if (str.equals("change weights"))     setText(weight); 
	else if (str.equals("remove arrows"))      setText(rmvarrows); 
	else if (str.equals("clear / reset"))      setText(clrreset); 
	else if (str.equals("run algorithm"))      setText(runalg); 
	else if (str.equals("step through"))       setText(step); 
	else if (str.equals("example"))            setText(example);  
        else if (str.equals("exit"))               setText(exitbutton); 
	else if (str.equals("all items"))          setText(doc); 
	else if (str.equals("toclose"))            setText(toclose);  
	else if (str.equals("done"))               setText(done);    
	else if (str.equals("locked"))             setText(locked); 
	else if (str.equals("maxnodes"))           setText(maxnodes);        
        else if (str.equals("none"))               setText(none);    
        else if (str.equals("some"))               setText(some);    
	else setText(str); 
    } 
} 
 
 
class GraphCanvas extends Canvas implements Runnable { 
// drawing area for the graph 
     
    final int MAXNODES = 20; 
    final int MAX = MAXNODES+1; 
    final int NODESIZE = 26; 
    final int NODERADIX = 13; 
    final int DIJKSTRA = 1; 
  
    // basic graph information 
    Point node[] = new Point[MAX];          // node 
    int weight[][] = new int[MAX][MAX];     // weight of arrow 
    Point arrow[][] = new Point[MAX][MAX];  // current position of arrowhead 
    Point startp[][] = new Point[MAX][MAX]; // start and 
    Point endp[][] = new Point[MAX][MAX];   // endpoint of arrow 
    float dir_x[][] = new float[MAX][MAX];  // direction of arrow 
    float dir_y[][] = new float[MAX][MAX];  // direction of arrow 
    
    // graph information while running algorithm 
    boolean algedge[][] = new boolean[MAX][MAX]; 
    int dist[] = new int[MAX]; 
    int finaldist[] = new int[MAX]; 
    Color colornode[] = new Color[MAX]; 
    boolean changed[] = new boolean[MAX];   // indicates distance change during algorithm    
    int numchanged =0;  
    int neighbours=0; 
     
    int step=0; 
     
    // information used by the algorithm to find the next  
    // node with minimum distance 
    int mindist, minnode, minstart, minend; 
 
    int numnodes=0;      // number of nodes 
    int emptyspots=0;    // empty spots in array node[] (due to node deletion) 
    int startgraph=0;    // start of graph 
    int hitnode;         // mouse clicked on or close to this node 
    int node1, node2;    // numbers of nodes involved in current action 
 
    Point thispoint=new Point(0,0); // current mouseposition 
    Point oldpoint=new Point(0, 0); // previous position of node being moved 
 
    // current action 
    boolean newarrow = false; 
    boolean movearrow = false; 
    boolean movestart = false; 
    boolean deletenode = false; 
    boolean movenode = false; 
    boolean performalg = false; 
    boolean clicked = false; 
 
    // fonts 
    Font roman= new Font("TimesRoman", Font.BOLD, 12); 
    Font helvetica= new Font("Helvetica", Font.BOLD, 15); 
    FontMetrics fmetrics = getFontMetrics(roman); 
    int h = (int)fmetrics.getHeight()/3; 
 
    // for double buffering 
    private Image offScreenImage; 
    private Graphics offScreenGraphics; 
    private Dimension offScreenSize; 
 
 
    // for run option 
    Thread algrthm; 
 
    // current algorithm, (in case more algorithms are added) 
    int algorithm; 
 
    // algorithm information to be displayed in documetation panel 
    String showstring = new String(""); 
 
    boolean stepthrough=false; 
 
    // locking the screen while running the algorithm 
    boolean Locked = false; 
 
    GraphAlgorithm parent; 
 
    GraphCanvas(GraphAlgorithm myparent) { 
	parent = myparent; 
	init(); 
	algorithm=DIJKSTRA; 
	setBackground(Color.white); 
    } 
 
    public void lock() { 
    // lock screen while running an algorithm 
	Locked=true; 
    } 
 
    public void unlock() { 
	Locked=false; 
    } 
 
    public void start() { 
	if (algrthm != null) algrthm.resume(); 
    } 
 
    public void init() { 
	for (int i=0;i0) 
	         arrowupdate(i, j, weight[i][j]); 
 
	repaint(); 
    } 
 
    public boolean mouseDown(Event evt, int x, int y) { 
	 
	if (Locked) 
	    parent.documentation.doctext.showline("locked"); 
	else { 
	  clicked = true; 
	  if (evt.shiftDown()) { 
	  // move a node 
	     if (nodehit(x, y, NODESIZE)) { 
	        oldpoint = node[hitnode]; 
	        node1 = hitnode; 
	        movenode=true; 
	     } 
	  } 
	  else if (evt.controlDown()) { 
	  // delete a node 
	     if (nodehit(x, y, NODESIZE)) { 
	        node1 = hitnode; 
	        if (startgraph==node1) { 
	           movestart=true; 
	           thispoint = new Point(x,y); 
                   colornode[startgraph]=Color.gray; 
	        } 
	        else 
	           deletenode= true; 
	     } 
	  } 
	  else if (arrowhit(x, y, 5)) { 
	  // change weihgt of an edge 
	     movearrow = true; 
	     repaint(); 
	  } 
	  else if (nodehit(x, y, NODESIZE)) { 
	  // draw a new arrow 
	     if (!newarrow) { 
	        newarrow = true; 
	        thispoint = new Point(x, y); 
	        node1 = hitnode; 
	     } 
	   } 
	   else if ( !nodehit(x, y, 50) && !arrowhit(x, y, 50) )  { 
	   // draw new node 
	      if (emptyspots==0) { 
	      // take the next available spot in the array 
	         if (numnodes < MAXNODES) 
	            node[numnodes++]=new Point(x, y); 
	         else  parent.documentation.doctext.showline("maxnodes"); 
	      } 
	      else { 
	      // take an empty spot in the array (from previously deleted node) 
	         int i; 
	         for (i=0;i0) { 
	            arrowupdate(i, node1, weight[i][node1]); 
	         } 
	         if (weight[node1][i]>0) { 
	            arrowupdate(node1, i, weight[node1][i]); 
	         } 
	      } 
	      repaint(); 
	   } 
	   else if (movestart || newarrow) { 
	      thispoint = new Point(x, y); 
	      repaint(); 
	   } 
	   else if (movearrow) { 
	      changeweight(x, y); 
	      repaint(); 
	   } 
	} 
	return true; 
    } 
 
 
    public boolean mouseUp(Event evt, int x, int y) { 
	if ( (!Locked) && clicked ) { 
	   if (movenode) { 
	   // move the node if the new position is not to close to  
	   // another node or outside of the panel 
	      node[node1]=new Point(0, 0); 
	      if ( nodehit(x, y, 50) || (x<0) || (x>this.size().width) ||  
					  (y<0) || (y>this.size().height) ) { 
	         node[node1]=oldpoint; 
	         parent.documentation.doctext.showline("toclose"); 
	      } 
	      else node[node1]=new Point(x, y); 
	      for (int i=0;i0) 
	            arrowupdate(i, node1, weight[i][node1]); 
	         if (weight[node1][i]>0)  
	            arrowupdate(node1, i, weight[node1][i]); 
	      } 
	      movenode=false; 
	   } 
	   else if (deletenode) { 
	      nodedelete(); 
	      deletenode=false; 
	   } 
	   else if (newarrow) { 
	      newarrow = false; 
	      if (nodehit(x, y, NODESIZE)) { 
	         node2=hitnode; 
	         if (node1!=node2) { 
	            arrowupdate(node1, node2, 50); 
	            if (weight[node2][node1]>0) { 
	               arrowupdate(node2, node1, weight[node2][node1]); 
	            } 
	            parent.documentation.doctext.showline("change weights"); 
	         } 
	         else System.out.println("zelfde"); 
	      } 
	   } 
	   else if (movearrow) { 
	      movearrow = false; 
	      if (weight[node1][node2]>0) 
	         changeweight(x, y); 
	   } 
	   else if (movestart) { 
	   // if new position is a node, this node becomes the startnode 
	      if (nodehit(x, y, NODESIZE)) 
	         startgraph=hitnode; 
	      colornode[startgraph]=Color.blue; 
	      movestart=false; 
	   } 
	   repaint(); 
	} 
	return true; 
    } 
 
    public boolean nodehit(int x, int y, int dist) { 
    // checks if you hit a node with your mouseclick 
	for (int i=0; i0 ) &&  
			(Math.pow(x-arrow[i][j].x, 2) +  
			 Math.pow(y-arrow[i][j].y, 2) < Math.pow(dist, 2) ) ) { 
	        node1 = i; 
	        node2 = j; 
	        return true; 
	     } 
	  } 
	return false; 
    } 
 
    public void nodedelete() { 
    // delete a node and the arrows coming into/outof the node 
	node[node1]=new Point(-100, -100); 
	for (int j=0;j 
		     Math.abs(endp[node1][node2].y-startp[node1][node2].y) ) { 
	   follow_x = true; 
	} 
 
	// find the new position of the arrowhead, and calculate  
	// the corresponding weight 
	if (follow_x) { 
	    int hbound = Math.max(startp[node1][node2].x,  
				  endp[node1][node2].x)-Math.abs(diff_x); 
	    int lbound = Math.min(startp[node1][node2].x,  
				  endp[node1][node2].x)+Math.abs(diff_x); 
 
	    // arrow must stay between the nodes 
	    if (xhbound) { arrow[node1][node2].x=hbound; } 
	    else arrow[node1][node2].x=x; 
 
	    arrow[node1][node2].y= 
		(arrow[node1][node2].x-startp[node1][node2].x) * 
		    (endp[node1][node2].y-startp[node1][node2].y)/ 
		        (endp[node1][node2].x-startp[node1][node2].x) +  
		startp[node1][node2].y; 
 
	    // new weight 
	    weight[node1][node2]= 
		Math.abs(arrow[node1][node2].x-startp[node1][node2].x-diff_x)* 
			100/(hbound-lbound); 
	} 
	// do the same if you follow y 
	else { 
	    int hbound = Math.max(startp[node1][node2].y,  
			 	  endp[node1][node2].y)-Math.abs(diff_y); 
	    int lbound = Math.min(startp[node1][node2].y,  
			 	  endp[node1][node2].y)+Math.abs(diff_y); 
 
	    if (yhbound) { arrow[node1][node2].y=hbound; } 
	    else arrow[node1][node2].y=y; 
	    arrow[node1][node2].x= 
		(arrow[node1][node2].y-startp[node1][node2].y) * 
		    (endp[node1][node2].x-startp[node1][node2].x)/ 
			(endp[node1][node2].y-startp[node1][node2].y) +  
		startp[node1][node2].x; 
 
	    weight[node1][node2]= 
		Math.abs(arrow[node1][node2].y-startp[node1][node2].y-diff_y)* 
			100/(hbound-lbound); 
	} 
    } 
 
    public void arrowupdate(int p1, int p2, int w) { 
    // make a new arrow from node p1 to p2 with weight w, or change 
    // the weight of the existing arrow to w, calculate the resulting  
    // position of the arrowhead 
	int dx, dy; 
	float l; 
	weight[p1][p2]=w; 
 
	// direction line between p1 and p2 
	dx = node[p2].x-node[p1].x; 
	dy = node[p2].y-node[p1].y; 
 
	// distance between p1 and p2 
	l = (float)( Math.sqrt((float)(dx*dx + dy*dy))); 
	dir_x[p1][p2]=dx/l; 
	dir_y[p1][p2]=dy/l; 
 
	// calculate the start and endpoints of the arrow, 
	// adjust startpoints if there also is an arrow from p2 to p1 
	if (weight[p2][p1]>0) { 
	   startp[p1][p2] = new Point((int)(node[p1].x-5*dir_y[p1][p2]),  
				      (int)(node[p1].y+5*dir_x[p1][p2])); 
	   endp[p1][p2] = new Point((int)(node[p2].x-5*dir_y[p1][p2]),  
				    (int)(node[p2].y+5*dir_x[p1][p2])); 
	} 
	else { 
	   startp[p1][p2] = new Point(node[p1].x, node[p1].y); 
	   endp[p1][p2] = new Point(node[p2].x, node[p2].y); 
	} 
 
	// range for arrowhead is not all the way to the start/endpoints 
	int diff_x = (int)(Math.abs(20*dir_x[p1][p2])); 
	int diff_y = (int)(Math.abs(20*dir_y[p1][p2])); 
 
	// calculate new x-position arrowhead 
	if (startp[p1][p2].x>endp[p1][p2].x) { 
	   arrow[p1][p2] = new Point(endp[p1][p2].x + diff_x + 
		(Math.abs(endp[p1][p2].x-startp[p1][p2].x) - 2*diff_x )* 
			(100-w)/100 , 0); 
	} 
	else { 
	   arrow[p1][p2] = new Point(startp[p1][p2].x + diff_x + 
		(Math.abs(endp[p1][p2].x-startp[p1][p2].x) - 2*diff_x )* 
			w/100, 0); 
	} 
 
	// calculate new y-position arrowhead 
	if (startp[p1][p2].y>endp[p1][p2].y) { 
	   arrow[p1][p2].y=endp[p1][p2].y + diff_y + 
		(Math.abs(endp[p1][p2].y-startp[p1][p2].y) - 2*diff_y )* 
			(100-w)/100; 
	} 
	else { 
	   arrow[p1][p2].y=startp[p1][p2].y + diff_y + 
		(Math.abs(endp[p1][p2].y-startp[p1][p2].y) - 2*diff_y )* 
			w/100; 
	} 
    } 
 
 
    public String intToString(int i) { 
	char c=(char)((int)'a'+i); 
	return ""+c; 
    } 
 
    public final synchronized void update(Graphics g) { 
    // prepare new image offscreen 
	Dimension d=size(); 
	if ((offScreenImage == null) || (d.width != offScreenSize.width) || 
			(d.height != offScreenSize.height)) { 
	    offScreenImage = createImage(d.width, d.height); 
	    offScreenSize = d; 
	    offScreenGraphics = offScreenImage.getGraphics(); 
	} 
	offScreenGraphics.setColor(Color.white); 
	offScreenGraphics.fillRect(0, 0, d.width, d.height); 
	paint(offScreenGraphics); 
	g.drawImage(offScreenImage, 0, 0, null); 
    } 
 
    public void drawarrow(Graphics g, int i, int j) { 
    // draw arrow between node i and node j 
	int x1, x2, x3, y1, y2, y3; 
 
	// calculate arrowhead 
	x1= (int)(arrow[i][j].x - 3*dir_x[i][j] + 6*dir_y[i][j]); 
	x2= (int)(arrow[i][j].x - 3*dir_x[i][j] - 6*dir_y[i][j]); 
	x3= (int)(arrow[i][j].x + 6*dir_x[i][j]); 
 
	y1= (int)(arrow[i][j].y - 3*dir_y[i][j] - 6*dir_x[i][j]); 
	y2= (int)(arrow[i][j].y - 3*dir_y[i][j] + 6*dir_x[i][j]); 
	y3= (int)(arrow[i][j].y + 6*dir_y[i][j]); 
 
	int arrowhead_x[] = { x1, x2, x3, x1 }; 
	int arrowhead_y[] = { y1, y2, y3, y1 }; 
 
	// if edge already chosen by algorithm change color 
	if (algedge[i][j]) g.setColor(Color.orange); 
	// draw arrow 
	g.drawLine(startp[i][j].x, startp[i][j].y, endp[i][j].x, endp[i][j].y); 
	g.fillPolygon(arrowhead_x, arrowhead_y, 4); 
 
	// write weight of arrow at an appropriate position 
	int dx = (int)(Math.abs(7*dir_y[i][j])); 
	int dy = (int)(Math.abs(7*dir_x[i][j])); 
	String str = new String("" + weight[i][j]); 
	g.setColor(Color.black); 
	if ((startp[i][j].x>endp[i][j].x) && (startp[i][j].y>=endp[i][j].y)) 
	  g.drawString( str, arrow[i][j].x + dx, arrow[i][j].y - dy); 
	if ((startp[i][j].x>=endp[i][j].x) && (startp[i][j].yendp[i][j].y)) 
	    g.drawString( str, arrow[i][j].x + dx,  
			       arrow[i][j].y + fmetrics.getHeight() ); 
    } 
 
 
    public void detailsDijkstra(Graphics g, int i, int j) { 
    // check arrow between node i and node j is amongst the arrows to 
    // choose from during this step of the algorithm 
    // check if node j has the next minimal distance to the startnode 
	if ( (finaldist[i]!=-1) && (finaldist[j]==-1) ) { 
	  g.setColor(Color.red); 
	  if ( (dist[j]==-1) || (dist[j]>=(dist[i]+weight[i][j])) ) { 
             if ( (dist[i]+weight[i][j])0) && (dist[i]!=-1) ) { 
	     String str = new String(""+dist[i]); 
	     g.drawString(str, node[i].x - (int)fmetrics.stringWidth(str)/2 -1,  
			       node[i].y + h); 
	     // string to distance to nodes on the documentation panel 
	     if (finaldist[i]==-1) { 
	        neighbours++;   
	        if (neighbours!=1) 
	           showstring = showstring + ", "; 
	        showstring = showstring + intToString(i) +"=" + dist[i]; 
	     }  
	  } 
        showstring = showstring + ". "; 
        
        if ( (step>1) && (numchanged>0) ) { 
           if (numchanged>1) 
              showstring = showstring + "Notice that the distances to "; 
           else showstring = showstring + "Notice that the distance to "; 
           for (int i=0; i1) 
              showstring = showstring + "have changed!\n"; 
           else showstring = showstring + "has changed!\n"; 
        } 
        else showstring = showstring + " ";  
	if (neighbours>1) {  
	// if there where more canditates explain why this node is taken   
 	  showstring = showstring + "Node " + intToString(minend) +  
				    " has the minimum distance.\n"; 
	  //check if there are other paths to minend.  
	  int newpaths=0; 
	  for (int i=0; i0) && (weight[i][minend]>0) && ( finaldist[i] == -1 ) ) 
	        newpaths++;  
	  if (newpaths>0)  
	     showstring = showstring + "Any other path to " + intToString(minend) +  
                   " visits another red node, and will be longer than " +  mindist + ".\n"; 
          else showstring = showstring +  
			     "There are no other arrows coming in to "+ 
			     intToString(minend) + ".\n"; 
	} 
	else { 
           boolean morenodes=false; 
           for (int i=0; i0 ) && ( finaldist[i] == -1 ) && ( weight[i][minend]>0 ) ) 
	        morenodes=true;  
           boolean bridge=false; 
           for (int i=0; i0 ) && ( finaldist[i] == -1 ) && ( weight[minend][i]>0 ) ) 
	        bridge=true;  
           if ( morenodes && bridge )  
              showstring = showstring + "Since this node forms a 'bridge' to "+ 
               "the remaining nodes,\nall other paths to this node will be longer.\n";      
           else if ( morenodes && (!bridge) )  
              showstring = showstring + "Remaining gray nodes are not reachable.\n"; 
           else showstring = showstring + "There are no other arrows coming in to "+ 
	       intToString(minend) + ".\n";  
        } 
	showstring = showstring + "Node " + intToString(minend) +  
	   " will be colored orange to indicate " + mindist +  
	   " is the length of the shortest path to " + intToString(minend) +".";  
	parent.documentation.doctext.showline(showstring);        
    } 
 
    public void detailsalg(Graphics g, int i, int j) { 
    // more algorithms can be added later 
	if (algorithm==DIJKSTRA) 
	    detailsDijkstra(g, i, j); 
    } 
 
    public void endstepalg(Graphics g) { 
    // more algorithms can be added later 
	if (algorithm==DIJKSTRA) 
	    endstepDijkstra(g); 
	if ( ( performalg ) && (mindist==0) ) { 
	   if (algrthm != null) algrthm.stop(); 
	   int nreachable = 0; 
	   for (int i=0; i 0) 
		 nreachable++; 
	   if (nreachable == 0) 
	      parent.documentation.doctext.showline("none"); 
           else if (nreachable< (numnodes-emptyspots-1)) 
	      parent.documentation.doctext.showline("some"); 
	   else 
	      parent.documentation.doctext.showline("done"); 
	}  
    } 
 
    public void paint(Graphics g) { 
        mindist=0; 
	minnode=MAXNODES; 
	minstart=MAXNODES; 
	minend=MAXNODES; 
        for(int i=0; i0) { 
	        // if algorithm is running then perform next step for this arrow 
	        if (performalg) 
	           detailsalg(g, i, j); 
	        drawarrow(g, i, j); 
	     } 
 
	// if arrowhead has been dragged to 0, draw it anyway, so the user 
	// will have the option to make it positive again 
	if (movearrow && weight[node1][node2]==0) { 
	  drawarrow(g, node1, node2); 
	  g.drawLine(startp[node1][node2].x, startp[node1][node2].y,  
		     endp[node1][node2].x, endp[node1][node2].y); 
	} 
 
	// draw the nodes 
	for (int i=0; i0) { 
	     g.setColor(colornode[i]); 
	     g.fillOval(node[i].x-NODERADIX, node[i].y-NODERADIX,  
			NODESIZE, NODESIZE); 
	  } 
 
	// reflect the startnode being moved 
	g.setColor(Color.blue); 
	if (movestart) 
	  g.fillOval(thispoint.x-NODERADIX, thispoint.y-NODERADIX,  
			NODESIZE, NODESIZE); 
 
 
	g.setColor(Color.black); 
	// finish this step of the algorithm 
	if (performalg) endstepalg(g); 
 
	// draw black circles around nodes, write their names to the screen 
	g.setFont(helvetica); 
	for (int i=0; i0) { 
	     g.setColor(Color.black); 
	     g.drawOval(node[i].x-NODERADIX, node[i].y-NODERADIX,  
			NODESIZE, NODESIZE); 
	     g.setColor(Color.blue); 
	     g.drawString(intToString(i), node[i].x-14, node[i].y-14); 
	  } 
    } 
}