commit a65c19be295bff1d93f4c8f68210bb6329805f0c Author: mercury Date: Wed Dec 3 17:38:51 2003 +0000 Initial revision diff --git a/ClearGraph.jpg b/ClearGraph.jpg new file mode 100644 index 0000000..000aa37 Binary files /dev/null and b/ClearGraph.jpg differ diff --git a/Graph.java b/Graph.java new file mode 100644 index 0000000..a7e941e --- /dev/null +++ b/Graph.java @@ -0,0 +1,535 @@ +import java.util.*; +import java.text.DecimalFormat; +import java.awt.Color; +import java.awt.Graphics; + +/** + * Graph.java, models graph management and creation.
+ * Implements: GraphInterface.
+ * @author Matt Markham
+ */ + +public class Graph implements GraphInterface +{ + //size integer variable to keep track of number of nodes in the graph + private int size; + //Double two-dimensional array for the adjacency matrix + private double[][] adjacent; + //boolean to determine whether a graph is directed + private boolean directed; + //Vertex storage list + private ArrayList vertexList; + //int to determin generic unweighted value + private final int UNWEIGHTED_VALUE = 1; + //temporary Arraylist for dfs + private ArrayList tempAL=new ArrayList(); + //ArrayList containing the current shortest path + private ArrayList path; + + +/** + * Default Constructor, creates an undirected graph.
+ */ + + public Graph () + { + size = 0; + adjacent = new double[size][size]; + vertexList = new ArrayList(); + directed=false; + path = new ArrayList(); + } + + +/** + * Constructor, takes a boolean to determine directed or undirected.
+ */ + + public Graph (boolean param) + { + size = 0; + adjacent = new double[size][size]; + vertexList = new ArrayList(); + directed=param; + path = new ArrayList(); + } + + public void makeEmpty() + { + size=0; + adjacent = new double[size][size]; + vertexList.clear(); + } + + public boolean isEmpty() + { + return (size==0); + } + + + public int numVertices() + { + return size; + } + + + public int numEdges() + { + int c=0; + for(int x=0; x + * Preconditions: must be passed a new size value.
+ * Postconditions: resizes adjacency matrix.
+ * Throws: None. + */ + + private void resizeAdjacent ( int size ) + { + + double[][] temp = new double[size][size]; + + for( int x = 0; x < size - 1; x++) + + for ( int y = 0; y < size - 1; y++ ) + + temp[x][y] = adjacent[x][y]; + + for ( int x = 0; x < size; x++ ) + { + + temp[x][size - 1] = Double.POSITIVE_INFINITY; + + temp[size - 1][x] = Double.POSITIVE_INFINITY; + + } + + adjacent = temp; + } + + public void addEdge(Comparable searchKey1, Comparable searchKey2, double weight) throws GraphException + { + for(int x=0; xsize) + throw new GraphException ("index out of bounds!"); + else + return (GraphNode)vertexList.get(index); + } + + public GraphNode getVertex(Comparable searchKey) throws GraphException + { + for (int x=0;x + * Preconditions: Must be passed searchKey.
+ * Postconditions: returns integer index of given searchKey.
+ * Throws:Nonde. + */ + + private int findVertex (Comparable searchKey) + { + for (int d=0; d + * Preconditions: None.
+ * Postconditions: Makes all vertices unmarked.
+ * Throws:None. + */ + + private void clearMarks() + { + for (int x=0;x index ) + newrow = row - 1; + if ( col > index ) + newcol = col - 1; + temp[newrow][newcol] = adjacent[row][col]; + } + } + } + adjacent = temp; + size--; + return (GraphNode) vertexList.remove ( index ); + } + + public ArrayList dfs (Comparable searchKey) throws GraphException + { + ArrayList dfsList = new ArrayList(); + dfsList=dfsRec(getVertex(searchKey), dfsList); + clearMarks(); + return dfsList; + } + + +/** + * Private recursive method called to generate a breadth-first search.
+ * Preconditions: Must be passed a graphNode and an ArrayList.
+ * Postconditions: Returns an Arraylist for the bfs.
+ * Throws:None. + */ + + private ArrayList dfsRec(GraphNode vertex, ArrayList searchRecList) + { + searchRecList.add(vertex); + vertex.setMarked(true); + int i=vertexList.indexOf(vertex); + for(int j=0; j + * Preconditions: A Graphics object and the radius of the vertecies.
+ * Postconditions: Draws all of the neccessary components onto the GraphScreen.
+ * @author Andrew Coleman
+ */ + public void draw ( Graphics g, int radius ) + { + /* draw all of the edges in orange */ + g.setColor ( Color.orange ); + for ( int i = 0; i < adjacent.length; i++ ) + { + int firstx = getVertex ( i ).getX(); + int firsty = getVertex ( i ).getY(); + String firstkey = getVertex ( i ).getKey().toString(); + for ( int j = 0; j < adjacent[i].length; j++ ) + { + if ( adjacent[i][j] != Double.POSITIVE_INFINITY ) + { + int secondx = ((GraphNode) getVertex ( j )).getX(); + int secondy = ((GraphNode) getVertex ( j )).getY(); + String secondkey = (String)((GraphNode) getVertex ( j )).getKey(); + DecimalFormat format = new DecimalFormat(); + format.setMaximumFractionDigits ( 3 ); + int difference = 10; + /* the final x and y coordinates for the string for each edge */ + int xcoord, ycoord; + if ( firstx < secondx ) + xcoord = (secondx - firstx) / 2 + firstx; + else + xcoord = (firstx - secondx) / 2 + secondx; + if ( firsty < secondy ) + ycoord = (secondy - firsty) / 2 + firsty; + else + ycoord = (firsty - secondy) / 2 + secondy; + /* if i < j, then the edge goes from j to i */ + if ( i < j ) + { + g.drawLine ( firstx, firsty - 10, secondx, secondy - 10 ); + g.drawString ( firstkey + " to " + secondkey + ", Weight: " + format.format ( adjacent[i][j] ), xcoord, ycoord - 11 ); + } + /* if i > j, then the edge goes from i to j */ + else if ( i > j ) + { + g.drawLine ( firstx, firsty + 10, secondx, secondy + 10 ); + g.drawString ( firstkey + " to " + secondkey + ", Weight: " + format.format ( adjacent[i][j] ), xcoord, ycoord + 11 ); + } + } + } + } + + /* draw the shortest path stored in green */ + g.setColor ( Color.green ); + for ( int i = 1; i < path.size(); i++ ) + { + int firstx = ((GraphNode) path.get ( i - 1 )).getX(); + int firsty = ((GraphNode) path.get ( i - 1 )).getY(); + int secondx = ((GraphNode) path.get ( i )).getX(); + int secondy = ((GraphNode) path.get ( i )).getY(); + g.drawLine ( firstx, firsty, secondx, secondy ); + } + + /* draw all of the vertices in dark gray with a light gray border and the comparable data in the middle */ + for ( int i = 0; i < numVertices(); i++ ) + { + g.setColor ( Color.lightGray ); + GraphNode temp = getVertex ( i ); + g.fillOval ( temp.getX() - radius, temp.getY() - radius, radius * 2, radius * 2 ); + g.setColor ( Color.darkGray ); + int difference = 2; + g.fillOval ( temp.getX() - radius + difference, temp.getY() - radius + difference, (radius - difference) * 2, (radius - difference) * 2 ); + g.setColor ( Color.white ); + g.drawString ( (String) temp.getKey(), temp.getX(), temp.getY() ); + } + } + + /** + * This method is used to set the arraylist containing the shortest path to be displayed.
+ * Preconditions: An arraylist containing a valid shortest path.
+ * Postconditions: Sets the current path to the given arraylist.
+ * @author Andrew Coleman
+ */ + public void setShortestPathDisplay ( ArrayList path ) + { + this.path = path; + } + + /** + * Used to calculate the shortest distance between two vertices using Djisktra's algorithm.
+ * Preconditions: Two comparables representing the data in the two vertices.
+ * Postconditions: Returns an ArrayList containing the GraphNodes in the path.
+ * Throws: Throws a GraphException if the comparables are the same or there is no connecting path.
+ * @author Andrew Coleman
+ */ + public ArrayList shortestPath ( Comparable firstkey, Comparable lastkey ) throws GraphException { + if ( firstkey.compareTo ( lastkey ) == 0 ) + throw new GraphException ( "Cannot find shortest path to same vertex!" ); + /* the arraylist containing the raw path array */ + ArrayList path = new ArrayList ( vertexList.size() ); + /* mark the first vertex */ + getVertex ( firstkey ).setMarked ( true ); + /* indexes of the first and last search keys */ + int firstindex = findIndex ( firstkey ); + int secondindex = findIndex ( lastkey ); + /* the row of the adjacency matrix at firstindex */ + double[] weight = new double[vertexList.size()]; + /* initialize the weight/path arrays */ + for ( int i = 0; i < weight.length; i++ ) + { + path.add ( vertexList.get ( i ) ); + weight[i] = adjacent[firstindex][i]; + /* if the weight is not infinity we must change the path to reflect the changes */ + if ( weight[i] < Double.POSITIVE_INFINITY ) + path.set ( i, getVertex ( firstkey ) ); + } + + /* loop through all the elements in the graph, must mark them all */ + for ( int i = 1; i < vertexList.size(); i++ ) + { + /* index of smallest vertex */ + int smallest = -1; + /* smallest weight thus far that is not marked */ + double smallestweight = Double.POSITIVE_INFINITY; + for ( int j = 0; j < weight.length; j++ ) + /* run through the weight array and find the smallest weight */ + if ( smallestweight >= weight[j] && !((GraphNode) vertexList.get ( j )).isMarked() ) + { + /* note smallest vertex */ + smallest = j; + smallestweight = weight[j]; + } + /* mark smallest vertex */ + GraphNode smallnode = (GraphNode) vertexList.get ( smallest ); + smallnode.setMarked ( true ); + /* update the weight/path arrays */ + for ( int j = 0; j < weight.length; j++ ) + /* if a new weight to that vertex is less than the current weight, change the weight in the array and change the path arraylist */ + if ( weight[j] > weight[smallest] + adjacent[smallest][j] ) + { + weight[j] = weight[smallest] + adjacent[smallest][j]; + path.set ( j, vertexList.get ( smallest ) ); + } + } + /* backwards path */ + ArrayList result = new ArrayList(); + GraphNode node = getVertex ( lastkey ); + int lastindex = findIndex ( lastkey ); + /* while the node is not the first node */ + while ( lastindex != firstindex ) + { + result.add ( node ); + node = (GraphNode) path.get ( lastindex ); + int newlastindex = findIndex ( node.getKey() ); + /* if the indexes are the same, then there is no way to get to that vertex */ + if ( newlastindex == lastindex ) + throw new GraphException ( "No connecting path!" ); + else + lastindex = newlastindex; + } + /* gotta add the first one */ + result.add ( getVertex ( firstkey ) ); + /* the result to return */ + ArrayList sortedresult = new ArrayList ( result.size() ); + /* reverse the arraylist */ + for ( int i = result.size() - 1; i >= 0; i-- ) + sortedresult.add ( result.get ( i ) ); + return sortedresult; + } + + /** + * This method finds the index of a given comparable object.
+ * Preconditions: A valid comparable key in the graph.
+ * Postcondtions: Returns the index of the key, or -1 if the key is not in the graph.
+ */ + private int findIndex ( Comparable key ) + { + for ( int i = 0; i < vertexList.size(); i++ ) + if ( ((GraphNode) vertexList.get ( i )).getKey().compareTo ( key ) == 0 ) + return i; + return -1; + } + +} diff --git a/GraphDriver.java b/GraphDriver.java new file mode 100644 index 0000000..59fc1c1 --- /dev/null +++ b/GraphDriver.java @@ -0,0 +1,28 @@ +import java.util.ArrayList; + +public class GraphDriver +{ + public static void main ( String[] args ) + { + Graph mygraph = new Graph(); + mygraph.addVertex ( new GraphNode ( "A" ) ); + mygraph.addVertex ( new GraphNode ( "B" ) ); + mygraph.addVertex ( new GraphNode ( "C" ) ); + mygraph.addVertex ( new GraphNode ( "D" ) ); + mygraph.addVertex ( new GraphNode ( "E" ) ); + + mygraph.addEdge ( "A", "D", 9.0 ); + mygraph.addEdge ( "A", "B", 8.0 ); + mygraph.addEdge ( "A", "E", 4.0 ); + mygraph.addEdge ( "B", "C", 1.0 ); + mygraph.addEdge ( "C", "B", 2.0 ); + mygraph.addEdge ( "C", "D", 3.0 ); + mygraph.addEdge ( "D", "C", 2.0 ); + mygraph.addEdge ( "D", "E", 7.0 ); + mygraph.addEdge ( "E", "C", 1.0 ); + + ArrayList bft = mygraph.shortestPath ( "A", "C" ); + for ( int i = 0; i < bft.size(); i++ ) + System.out.println ( bft.get ( i ).toString() ); + } +} diff --git a/GraphException.java b/GraphException.java new file mode 100644 index 0000000..a7a1e3d --- /dev/null +++ b/GraphException.java @@ -0,0 +1,11 @@ +public class GraphException extends RuntimeException +{ + public GraphException() + { + } // end default constructor + + public GraphException(String s) + { + super(s); + } // end constructor +} // end GraphException \ No newline at end of file diff --git a/GraphGUI.java b/GraphGUI.java new file mode 100644 index 0000000..a0e575d --- /dev/null +++ b/GraphGUI.java @@ -0,0 +1,93 @@ +import javax.swing.*; +import java.awt.event.*; +import java.awt.*; +import java.util.*; + +/** This is the parent window in the graph, it only sets flags based on menu choices and provides a place for the GraphScreen to draw itself */ +public class GraphGUI extends JFrame { + + /** the height of the frame */ + private final int HEIGHT = 550; + + /** the width of the frame */ + private final int WIDTH = 640; + + /** the JPanel that does all of the work */ + private GraphScreen graphscreen; + + /** the status bar */ + private JLabel information; + + /** + * The default constructor.
+ * Preconditions: None.
+ * Postconditions: Sets up the window and displays everything.
+ */ + public GraphGUI() { + super ( "GraphGUI" ); + setSize ( WIDTH, HEIGHT ); + getContentPane().setLayout ( new BorderLayout() ); + + addWindowListener ( new WindowAdapter() { + public void windowClosing ( WindowEvent event ) { + System.exit ( 0 ); + } + }); + + /* Center the window in the middle of the screen */ + Dimension screensize = getToolkit().getScreenSize(); + int screenwidth = screensize.width; + int screenheight = screensize.height; + setLocation ( screenwidth / 2 - WIDTH / 2, screenheight / 2 - HEIGHT / 2); + + /* These are the individual menu items in the menu bar. */ + JMenuItem cleargraph = new JMenuItem ( new ImageIcon ( "ClearGraph.jpg" ) ); + cleargraph.setBackground ( Color.black ); + cleargraph.setToolTipText ( "Makes a new, empty, pretty graph." ); + cleargraph.addActionListener ( new ActionListener() { + public void actionPerformed ( ActionEvent event ) { + graphscreen.clearGraph(); + } + }); + JMenuItem shortestpath = new JMenuItem ( new ImageIcon ( "ShortestPath.jpg" ) ); + shortestpath.setBackground ( Color.black ); + shortestpath.setToolTipText ( "Determines the shortest path between two vertices." ); + shortestpath.addActionListener ( new ActionListener() { + public void actionPerformed ( ActionEvent event ) { + graphscreen.findShortestPath(); + } + }); + JMenuItem quit = new JMenuItem ( new ImageIcon ( "Quit.jpg" ) ); + quit.setBackground ( Color.black ); + quit.setToolTipText ( "All your base are belong to us." ); + quit.addActionListener ( new ActionListener() { + public void actionPerformed ( ActionEvent event ) { + System.exit ( 0 ); + } + }); + + /* the menu that keeps the menuitems */ + JMenu graph = new JMenu ( "GraphGUI" ); + graph.add ( cleargraph ); + graph.add ( shortestpath ); + graph.add ( quit ); + JMenuBar toolbar = new JMenuBar(); + toolbar.add ( graph ); + setJMenuBar ( toolbar ); + + /* set up the two remaining graphical components */ + information = new JLabel ( "Ready" ); + graphscreen = new GraphScreen ( information ); + + getContentPane().add ( graphscreen, BorderLayout.CENTER ); + getContentPane().add ( information, BorderLayout.SOUTH ); + + setResizable ( false ); + setVisible ( true ); + } + + /** Start the party */ + public static void main ( String[] args ) { + GraphGUI mygraph = new GraphGUI(); + } +} diff --git a/GraphInterface.java b/GraphInterface.java new file mode 100644 index 0000000..57682c2 --- /dev/null +++ b/GraphInterface.java @@ -0,0 +1,142 @@ +import java.util.*; + +public interface GraphInterface +{ + + //two constructors + //default constructor makes a directed graph + //constructor that takes a boolean (true for directed) + + +/** + * Makes the current graph empty.
+ * Preconditions: None.
+ * Postconditions: makes graph empty.
+ * Throws: None. + */ + public void makeEmpty(); + +/** + * Indicates if graph is empty.
+ * Preconditions: None.
+ * Postconditions: returns boolean true if graph is empty, false if not.
+ * Throws: None. + */ + public boolean isEmpty(); + +/** + * Returns an integer corresponding to thenumber of vertices in the graph.
+ * Preconditions: None.
+ * Postconditions: returns int.
+ * THrows: None. + */ + public int numVertices(); + +/** + * Returns an integer corresponding to the numberof edges in the graph.
+ * Preconditions: None.
+ * Postconditions: returns int.
+ * Throws: NOne. + */ + public int numEdges(); + +/** + * Adds a vertex to the graph.
+ * Preconditions: Must be passed GraphNode to be inserted into the graph.
+ * Postconditions: Adds the passed-in node to the graph.
+ * Throws: None. + */ + + //add a vertex to the Graph + public void addVertex(GraphNode myItem) throws GraphException; + +/** + *Adds an edge to the Graph.
+ *Preconditions: must be passed the searchkeys of the nodes that are to be connected.
+ *Postconditions: adds edge to graph.
+ *Throws: GraphException if searchkeys don't exist. + */ + + public void addEdge(Comparable searchKey1, Comparable searchKey2) throws GraphException; + +/** + * Adds a weighted edge to the graph.
+ * Preconditions: must be passed two search keys and a weight.
+ * Postconditions: adds weighted edge to the graph.
+ * Throws: GraphException if given invalid endpoints or weight value + */ + + public void addEdge(Comparable searchKey1, Comparable searchKey2, double weight) throws GraphException; + +/** + * Retrieves the weight of a given edge.
+ * Preconditions: must be given searchkeys of the endpoints of the edge.
+ * Postconditions: returns double value for the weight.
+ * Throws: GraphException if given invalid endpoints. + */ + + public double getWeight(Comparable searchKey1, Comparable searchKey2) throws GraphException; + +/** + * Removes an edge from the graph.
+ * Preconditions: must be passed searchKey valued for edge's endpoints.
+ * Postconditions: removes edge from graph.
+ * Throws: GraphException if invalid vertices. + */ + + public void removeEdge(Comparable searchKey1, Comparable searchKey2) throws GraphException; + +/** + * Removes a vetex from the graph.
+ * Preconditions: must be passed a searchKey to delete.
+ * Postconditions: removes given searchKey.
+ * Throws: GraphException if given invalid searchkey. + */ + + public GraphNode removeVertex(Comparable searchKey) throws GraphException; + +/** + * Gets vertex from graph.
+ * Preconditions: must be passed a valid searchKey.
+ * Postconditions: returns a graphNode for the given searchKey.
+ * Throws: GraphException if given an invaid searchKey. + */ + + public GraphNode getVertex(Comparable searchKey) throws GraphException; + +/** + * Gets vertex from graph.
+ * Preconditions: must be passed integer index of node in list.
+ * Postconditions: returns GraphNode at given index.
+ * Throws: GraphException if given invalid index value. + */ + + public GraphNode getVertex(int index) throws GraphException; + +/** + * Obtains a depth-first search of the grap from the given starting vertex.
+ * Preconditions: must be passed a searchKey for the starting point of the search.
+ * Postconditions: returns ArrayList of vertices along the search path.
+ * Throws: GraphException if given invalid searchKey + */ + + public ArrayList dfs(Comparable searchKey) throws GraphException; + +/** + * Obtains a breadth-first search of the graph.
+ * Preconditions: must be passed searchKey of the starring point of the search.
+ * Postconditions: returns an ArrayList of the vertices in the search.
+ * Throws: GraphException if the searchKey is invalid. + */ + + public ArrayList bfs(Comparable searchKey) throws GraphException; + +/** + * Returns the shortest path between two vertices in the graph.
+ * Preconditions: must be passed searchKeys of the starting and ending vertices.
+ * Postconditions: returns an ArrayList of vertices between the starting and ending search Keys.
+ * Throws: GraphException if enteres searchKeys are invalid. + */ + public ArrayList shortestPath(Comparable searchKey1, Comparable searchKey2) throws GraphException; + +} \ No newline at end of file diff --git a/GraphNode.java b/GraphNode.java new file mode 100644 index 0000000..2b6c3a5 --- /dev/null +++ b/GraphNode.java @@ -0,0 +1,81 @@ +/** + * GraphNode.java, an object that stores information about a graph node for + * Tree Applicatons.
+ * Extends: KeyedItem.
+ * Author: Matt Markham + */ + +public class GraphNode extends KeyedItem +{ + private int xCoord, yCoord; + boolean mark; + + GraphNode (Comparable item) + { + super(item); + mark=false; + } + + GraphNode (Comparable item, int x, int y) + { + super(item); + xCoord=x; + yCoord=y; + mark=false; + } + +/** + * Returns the x-coordinate of the current Graph Node.
+ * Preconditions: None.
+ * Postconditions: Returns int value of this nodes x position.
+ * Throws: None + */ + + + public int getX() + { + return xCoord; + } + +/** + * Returns the y-coordiante of the current Graph Node.
+ * Preconditions: None.
+ * Postconditions: Returns int value of this nodes y position.
+ * Throws: None + */ + + public int getY() + { + return yCoord; + } + +/** + * Returns true if node is marked, false if it is not.
+ * Preconditions: None.
+ * Postconditions: returns true if node is marked, false if it is not. + * Throws: None. + */ + + public boolean isMarked() + { + return mark; + } + + +/** + * Sets value of mark to true or false.
+ * Preconditions: Must be passed in a boolean.
+ * Postconditions: Sets mark to passed-in boolean.
+ * Throws: None. + */ + + public void setMarked(boolean setbool) + { + mark=setbool; + } + + public boolean equals (GraphNode node) + { + return ((node.getX() == xCoord)&&(node.getY()==yCoord) && (node.getKey().compareTo(this.getKey())==0)); + } +} \ No newline at end of file diff --git a/GraphScreen.java b/GraphScreen.java new file mode 100644 index 0000000..910621c --- /dev/null +++ b/GraphScreen.java @@ -0,0 +1,267 @@ +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.text.DecimalFormat; + +/** This class defines a custom JPanel that will draw vertices, edges, and the shortest path of a Graph object. This custom JPanel is used because it is much easier to intercept x and y coordinates in this panel than in the frame and then having to compensate for the other graphical components. */ + +public class GraphScreen extends JPanel implements MouseListener { + /** defines the radius of the vertices in pixels */ + private final int radius = 20; + + /** the path of vertices for the currently displayed shortest path */ + private ArrayList path; + + /** the graph to add vertices/edges to */ + private Graph mygraph; + + /** the status bar in the parent jframe */ + private JLabel information; + + /* a flag to check if you are calculating the shortest path */ + private boolean shortestpath; + + /* a flag used to check if the mouseclick intercepted was the second in the series (add edge, shortest path) */ + private boolean issecondvertex; + + /* the data stored in the first vertex clicked in the series */ + private Comparable firstvertex; + + + /** + * Default constructor for the GraphScreen panel.
+ * Preconditions: A JLabel status bar in the parent JFrame.
+ * Postconditions: Initializes all variables.
+ */ + public GraphScreen ( JLabel information ) { + super ( true ); + this.information = information; + addMouseListener ( this ); + mygraph = new Graph(); + mygraph.setShortestPathDisplay ( new ArrayList() ); + information.setText ( " Click to add vertex" ); + issecondvertex = false; + firstvertex = null; + shortestpath = false; + } + + /** mousePressed and mouseReleased could be used to move vertices around the graph, + but as per explicitly stated in the program requirements, the GraphNodes have no + setX ( int x ) and setY ( int y ) methods. */ + public void mousePressed ( MouseEvent event ) {} + public void mouseReleased ( MouseEvent event ) {} + + /** not used, just here to fully implement the MouseListener interface */ + public void mouseEntered ( MouseEvent event ) {} + public void mouseExited ( MouseEvent event ) {} + + /** + * Intercepts a mouse click in the drawing window.
+ * Preconditions: A mouse click.
+ * Postconditions: This method could add a vertex, add an edge, or calculate where the shortest path needs to be based upon flags and user interaction.
+ */ + public void mouseClicked ( MouseEvent event ) { + /* If it is a right mouse click, then we must add an edge */ + if ( SwingUtilities.isRightMouseButton ( event ) ) { + /* the node that is contained within radius pixels of the MouseEvent */ + GraphNode temp = findVertex ( event.getX(), event.getY() ); + /* if temp is null then the user clicked in blank space */ + if ( temp != null ) { + /* if this is the second right mouse click, and it is not the same vertex */ + if ( issecondvertex && firstvertex.compareTo ( temp.getKey() ) != 0 ) { + /* lets add an edge, and hopefully it will work */ + try { + addEdge ( firstvertex, temp.getKey() ); + information.setText ( " Edge added between " + firstvertex.toString() + " and " + temp.getKey().toString() ); + } + /* error exists between keyboard and chair */ + catch ( GraphException exception ) { + information.setText ( " Could not add the edge" ); + JOptionPane.showMessageDialog ( null, exception.toString(), "Exception caught", JOptionPane.WARNING_MESSAGE ); + } + /* we will set all the flags to normal */ + issecondvertex = false; + firstvertex = null; + } + /* this must be the first right click intercepted */ + else { + /* save the data for use later */ + firstvertex = temp.getKey(); + issecondvertex = true; + information.setText ( " Right click on another vertex to create an edge" ); + } + } + /* we don't care and set everything to normal since the user clicked in blank space */ + else { + firstvertex = null; + issecondvertex = false; + information.setText ( " Right click on vertices to add and edge" ); + } + } + + /* not the right mouse button, so this could be shortest path or add a vertex */ + /* if the shortest path flag is set, we must find two different vertices */ + else if ( shortestpath ) { + /* if there are less than 2 vertices in the graph, it is impossible to find the shortest path */ + if ( mygraph.numVertices() > 2 ) { + GraphNode temp = findVertex ( event.getX(), event.getY() ); + /* if this is the second click and the user clicked on a node and the node is not the same node */ + if ( issecondvertex && temp != null && firstvertex.compareTo ( temp.getKey() ) != 0 ) { + /* set the shortest path arraylist to the path in the graph */ + try { + mygraph.setShortestPathDisplay ( mygraph.shortestPath ( firstvertex, temp.getKey() ) ); + information.setText ( " Shortest path is displayed in green" ); + repaint(); + } + catch ( GraphException exception ) { + JOptionPane.showMessageDialog ( null, exception.toString(), "Exception caught", JOptionPane.WARNING_MESSAGE ); + mygraph.setShortestPathDisplay ( new ArrayList() ); + information.setText ( " Could not create the shortest path" ); + } + shortestpath = false; + issecondvertex = false; + firstvertex = null; + } + /* the user is clicking on blank space */ + else if ( temp == null ) { + information.setText ( " You must first click on vertices" ); + shortestpath = false; + issecondvertex = false; + firstvertex = null; + } + /* must be the first click in the series then */ + else { + firstvertex = temp.getKey(); + issecondvertex = true; + information.setText ( " Click on the ending vertex of the path" ); + } + } + /* not enough vertices to make a shortest path */ + else { + information.setText ( " There must be at least 2 vertices in the graph before calculating the shortest path" ); + shortestpath = false; + firstvertex = null; + issecondvertex = false; + } + } + + /* well, the only thing left is to add a new vertex */ + else { + /* if the user clicked in blank space for once */ + if ( findVertex ( event.getX(), event.getY() ) == null ) { + information.setText ( " Click to add vertex" ); + shortestpath = false; + issecondvertex = false; + firstvertex = null; + /* ask the user what they want to call it */ + String name = JOptionPane.showInputDialog ( null, "Enter name: ","Add new graph node", JOptionPane.QUESTION_MESSAGE ); + /* as long as the name appears somewhat valid anyways */ + if ( name != null && !name.equals ( "" ) ) { + int x = event.getX(), y = event.getY(); + /* these if's check to make sure the user didn't click too close to the edge */ + if ( x + radius > getWidth() ) + x = getWidth() - radius; + if ( x - radius < 0 ) + x = radius; + if ( y - radius > getHeight() ) + y = getHeight() - radius; + if ( y - radius < 0 ) + y = radius; + try { + mygraph.addVertex ( new GraphNode ( name, x, y ) ); + information.setText ( " Vertex added at (" + x + ", " + y + ")" ); + repaint(); + } + catch ( Exception exception ) { + JOptionPane.showMessageDialog ( null, exception.toString(), "Exception caught!", JOptionPane.WARNING_MESSAGE ); + information.setText ( " Vertex could not be added" ); + } + } + } + /* the user clicked on a vertex */ + else { + information.setText ( " Vertex already added at location" ); + JOptionPane.showMessageDialog ( null, "Vertex is already at that point!", "Waitaminit!", JOptionPane.WARNING_MESSAGE ); + } + } + + } + + /** + * Empties the graph and sets all flags to default values.
+ * Preconditions: None.
+ * Postconditions: Resets all internally used variables to default values.
+ */ + public void clearGraph() { + mygraph.makeEmpty(); + information.setText ( " Click to add vertex" ); + mygraph.setShortestPathDisplay ( new ArrayList() ); + issecondvertex = false; + firstvertex = null; + shortestpath = false; + repaint(); + } + + /** + * Used to set the flag to find the shortest path.
+ * Preconditions: None.
+ * Postconditions: Sets the shortestpath flag to true.
+ */ + public void findShortestPath() { + shortestpath = true; + information.setText ( " Click on the starting vertex" ); + } + + /** + * Attempts to locate a GraphNode within x +/- radius pixels and y +/- radius pixels of the given coordinate.
+ * Preconditions: Two integers representing coordinates inside the GraphScreen.
+ * Postconditions: Returns null if no GraphNode is close to the coord. or the GraphNode closest to that point.
+ */ + private GraphNode findVertex ( int x, int y ) { + /* look through all of the nodes one by one */ + for ( int i = 0; i < mygraph.numVertices(); i++ ) { + GraphNode result = mygraph.getVertex ( i ); + int tempx = result.getX(); + int tempy = result.getY(); + if ( tempx + radius >= x && tempx - radius <= x && tempy - radius <= y && tempy + radius >= y ) + return result; + } + return null; + } + + /** + * Adds an edge between two vertices.
+ * Preconditions: Two comparable objects that are in the graph.
+ * Postconditions: Adds an edge between the two vertices containing the comparable objects.
+ */ + public void addEdge ( Comparable first, Comparable second ) { + String temp = JOptionPane.showInputDialog ( null, "Enter a weight", "Add an edge", JOptionPane.QUESTION_MESSAGE ); + double result = -1.0; + if ( temp != null ) { + try { + result = Double.parseDouble ( temp ); + if ( result < 0 ) + throw new GraphException ( "Invalid weight for edge." ); + mygraph.addEdge ( first, second, result ); + information.setText ( " New edge added between vertices {" + (String) first + "} and {" + (String) second + "}" ); + repaint(); + } + catch ( Exception exception ) { + information.setText ( " Edge could not be added" ); + JOptionPane.showMessageDialog ( null, exception.toString(), "Exception Caught", JOptionPane.WARNING_MESSAGE ); + } + } + } + + + /** + * Draws the background for the custom JPanel.
+ */ + public void paint ( Graphics g ) { + /* draw a black background */ + g.setColor ( Color.black ); + g.fillRect ( 0, 0, this.getWidth(), this.getHeight() ); + mygraph.draw ( g, radius ); + } +} diff --git a/KeyedItem.java b/KeyedItem.java new file mode 100644 index 0000000..e6fa1a7 --- /dev/null +++ b/KeyedItem.java @@ -0,0 +1,38 @@ +public abstract class KeyedItem +{ + private Comparable searchKey; + + public KeyedItem(Comparable key) + { + searchKey = key; + } // end constructor + + public Comparable getKey() + { + return searchKey; + } // end getKey + + public String toString() + { + return searchKey.toString(); + } + +} // end KeyedItem + + +/* +How to use: +public class CD extends KeyedItem +{ + //title not present here + String artist; + ... + public CD(String title, String artist, double price, int tracks) + { + super(title); + this.artist=artist; + ... + } + +} +*/ \ No newline at end of file diff --git a/Node.java b/Node.java new file mode 100644 index 0000000..370b798 --- /dev/null +++ b/Node.java @@ -0,0 +1,39 @@ +public class Node +{ + private Object item; + private Node next; //reference to another node + + public Node(Object item) + { + this.item = item; + next = null; + } // end constructor + + public Node(Object item, Node next) + { + this.item = item; + this.next = next; + } // end constructor + + public void setItem(Object item) + { + this.item = item; + } // end setItem + + public Object getItem() + { + return item; + } // end getItem + + public void setNext(Node next) + { + this.next = next; + } // end setNext + + public Node getNext() + { + return next; + } // end getNext + +} // end class Node + diff --git a/QueueException.java b/QueueException.java new file mode 100644 index 0000000..f904660 --- /dev/null +++ b/QueueException.java @@ -0,0 +1,7 @@ +public class QueueException extends RuntimeException +{ + public QueueException(String s) + { + super(s); + } // end constructor +} // end QueueException \ No newline at end of file diff --git a/QueueInterface.java b/QueueInterface.java new file mode 100644 index 0000000..3c24f86 --- /dev/null +++ b/QueueInterface.java @@ -0,0 +1,57 @@ +public interface QueueInterface +{ + /** + * Determines whether a queue is empty. + * Precondition: None. + * Postcondition: Returns true if the queue is empty; + * otherwise returns false. + */ + public boolean isEmpty(); + + /** + * Adds an item at the back of a queue. + * Precondition: item is the item to be inserted. + * Postcondition: If the operation was successful, newItem + * is at the back of the queue. Some implementations + * may throw QueueException if item cannot be added to the queue. + */ + public void enqueue(Object item) throws QueueException; + + /** + * Retrieves and removes the front of a queue. + * Precondition: None. + * Postcondition: If the queue is not empty, the item + * that was added to the queue earliest is returned and + * the item is removed. If the queue is empty, the + * operation is impossible and QueueException is thrown. + */ + public Object dequeue() throws QueueException; + + /** + * Removes all items of a queue. + * Precondition: None. + * Postcondition: The queue is empty. + */ + public void dequeueAll(); + + /** + * Retrieves the item at the front of a queue. + * Precondition: None. + * Postcondition: If the queue is not empty, the item + * that was added to the queue earliest is returned. + * If the queue is empty, the operation is impossible + * and QueueException is thrown. + */ + public Object peek() throws QueueException; + + /** + * Determines the length of a queue. + * Precondition: None. + * Postcondition: Returns the number of items that are + * currently in the queue. + * Throws: None. + */ + public int size(); + + +} // end QueueInterface \ No newline at end of file diff --git a/QueueReferenceBased.java b/QueueReferenceBased.java new file mode 100644 index 0000000..7485cf1 --- /dev/null +++ b/QueueReferenceBased.java @@ -0,0 +1,92 @@ +public class QueueReferenceBased implements QueueInterface +{ + // circular references + private Node tail; + private int size; + + public QueueReferenceBased() + { + tail = null; + size=0; + } // end default constructor + + // queue operations: + public boolean isEmpty() + { + return tail == null; + } // end isEmpty + + public int size() + { + return size; + } + + public void dequeueAll() + { + if (tail!=null) + tail.setNext(null); + + tail = null; + size=0; + } // end dequeueAll + + public void enqueue(Object item) + { + Node myNode = new Node(item); + + // insert the new node + if (isEmpty()) + { + // insertion into empty queue + myNode.setNext(myNode); + } + else + { + // insertion into nonempty queue + myNode.setNext(tail.getNext()); + tail.setNext(myNode); + } // end if + + tail = myNode; // new node is at back + size++; + } // end enqueue + + public Object dequeue() throws QueueException + { + if (!isEmpty()) + { + // queue is not empty; remove front + Node head = tail.getNext(); + if (head == tail) + { // special case? + tail.setNext(null); + tail = null; // yes, one node in queue + } + else + { + tail.setNext(head.getNext()); + } // end if + size--; + return head.getItem(); + } + else + { + throw new QueueException("Queue empty"); + } // end if + } // end dequeue + + public Object peek() throws QueueException + { + if (!isEmpty()) + { + // queue is not empty; retrieve front + Node head = tail.getNext(); + return head.getItem(); + } + else + { + throw new QueueException("Queue empty"); + } // end if + } // end peek + +} // end QueueCircular \ No newline at end of file diff --git a/Quit.jpg b/Quit.jpg new file mode 100644 index 0000000..62b3ece Binary files /dev/null and b/Quit.jpg differ diff --git a/ShortestPath.jpg b/ShortestPath.jpg new file mode 100644 index 0000000..437ea45 Binary files /dev/null and b/ShortestPath.jpg differ