CSC212 Lab 15 Solution 2014

From dftwiki3
Jump to: navigation, search

--D. Thiebaut (talk) 11:22, 5 December 2014 (EST)




The text of Lab 15 is available here. You need to setup Eclipse to work with Processing to make the code below work.


Model


import java.util.ArrayList;
import java.util.Random;

import processing.core.PApplet;

/**
 * Model component of Model-View-Controller system. Implements the graph, holds
 * the vertices (numbered 0 to maxNoVertices) and keeps the edges in a boolean
 * adjacency matrix, that is maxNoVertices x maxNoVertices;
 * 
 * @author D. Thiebaut
 *
 */

public class Lab15_Model  {
	private final int maxNoVerticesRows = 50; // geometry
	private final int maxNoVerticesCols = 50; // 50 x 50 grid = 2500 vertices
												// total
	private int maxNoVertices = maxNoVerticesRows * maxNoVerticesCols;
	private boolean[] visited;
	private boolean[][] adjMat = null; // adjacency matrix
	private int probability100 = 35; // probability (as percentage) for an edge
										// (35 means 35%)

	private Lab15_Viewer viewer = null;// reference to the viewer of the MVC
										// system
	private Lab15_Controller controller = null;// reference to the controller of
												// the MVC system
	
	/**
	 * constructor. Nothing to do. The building of the network is done by
	 * initGraph().
	 */
	public Lab15_Model() {
	}

	/**
	 * mutator. Sets the reference to the controller.
	 * 
	 * @param c
	 */
	public void setController(Lab15_Controller c) {
		controller = c;
	}

	/**
	 * mutator. Sets the reference to the viewer.
	 * 
	 * @param v
	 */
	public void setViewer(Lab15_Viewer v) {
		viewer = v;
	}

	/**
	 * accessor
	 * 
	 * @return number of vertices in graph
	 */
	public int getNoVertices() {
		return maxNoVertices;
	}

	public int getNoCols() {
		return maxNoVerticesCols;
	}

	public int getNoRows() {
		return maxNoVerticesRows;
	}

	/**
	 * add an undirected edge between u and v.
	 * 
	 * @param v
	 * @param u
	 */
	public void addEdge(int v, int u) {
		adjMat[v][u] = true;
		adjMat[u][v] = true;
	}

	/**
	 * returns vertex at given row and col. Assumes row & col will always be
	 * valid.
	 * 
	 * @param row
	 * @param col
	 * @return the vertex at row, col
	 */
	private int vertexAtRowCol(int row, int col) {
		return row + col * maxNoVerticesRows;
	}

	/**
	 * generate a random graph. The seed of the random number generator is set
	 * to a fixed number, so that the graph generated is always the same.
	 */
	public void initGraph() {
		Random random = new Random();
		random.setSeed(12345); // seed for random number generator
		
		adjMat = new boolean[maxNoVertices][maxNoVertices];
		visited = new boolean[maxNoVertices]; // automatically initialized to false
		
		for (int r = 1; r < maxNoVerticesRows; r++) {
			for (int c = 1; c < maxNoVerticesCols; c++) {
				int currentVertex = vertexAtRowCol(r, c);
				int upVertex = vertexAtRowCol(r - 1, c);
				int leftVertex = vertexAtRowCol(r, c - 1);

				// should we create an up connection?
				if (random.nextInt(100) <= probability100)
					addEdge(currentVertex, upVertex);

				// should we create a left connection?
				if (random.nextInt(100) <= probability100)
					addEdge(currentVertex, leftVertex);
			}
		}
	}

	/**
	 * returns the adjacency list of Vertex v in an arrayList.
	 * 
	 * @param v
	 *            for which we want the adjacent vertices.
	 * @return an arrayList of vertices. The vertices numbers are listed in
	 *         increasing order.
	 */
	public ArrayList<Integer> adjList(int v) {
		ArrayList<Integer> adj = new ArrayList<Integer>();
		for (int u = 0; u < maxNoVertices; u++)
			if (adjMat[v][u])
				adj.add(u);
		return adj;
	}
	
	/**
	 * executes a DFS starting on Vertex v 
	 * @param vertex
	 */
	public void DFS( int vertex ) {
		visited = new boolean[ maxNoVertices ];
		recurseDFS( vertex );
	}
	
	/**
	 * recursively visit vertex v and all the vertices u adjacent to v
	 * @param v
	 */
	private void recurseDFS( int v ) {
		//System.out.println( "recurseDFS on " + v );
		visited[v] = true;
		for ( int u: adjList( v ) ) 
			if ( !visited[u] )
				recurseDFS( u );
	}

	public boolean isVisited(int v) {
		return visited[v];
	}

}


Controller


/**
 * MVC1_controller. The controller of a simple Model-View-Controller (MVC)
 * system. The MVC system displays a 50x50 network of vertices with left, right,
 * up, and down edges to their neighbors. The controller is started by the
 * viewer, which extends PApplet, and which is the first object to become alive.
 * The controller starts the model which initializes the graph. Every graphical
 * interaction in the viewer is passed on to the controller.
 * 
 * @author D. Thiebaut
 *
 */

public class Lab15_Controller {
	// links to the viewer and model.
	Lab15_Viewer viewer = null;
	Lab15_Model model = null;

	/**
	 * constructor.
	 */
	public Lab15_Controller() {
	}

	/**
	 * mutator
	 * 
	 * @param v
	 *            reference to the viewer
	 */
	public void setViewer(Lab15_Viewer v) {
		viewer = v;
	}

	/**
	 * mutator
	 * 
	 * @param m
	 *            reference to the model
	 */
	public void setModel(Lab15_Model m) {
		model = m;
	}

	/**
	 * launched by the viewer, which passes a reference to itself in the
	 * process. Creates a set of references between the model, the view, and the
	 * controller. Initializes the graph held by the model (vertices and edges)
	 * 
	 * @param v
	 *            reference to the viewer.
	 */
	public void initAll(Lab15_Viewer v) {
		// init viewer
		viewer = v;

		// create the model
		model = new Lab15_Model();

		// create reference system between MVC components
		model.setController(this);
		viewer.setModel(model);
		model.setViewer(viewer);

		// create the graph of vertices and edges
		model.initGraph();
	}

	/**
	 * called by viewer when the mouse is over a given vertex. decides what
	 * viewer should do, depending on different modes of operation.
	 * 
	 * @param vertex
	 */
	public void setVertexUnderMouse(int vertex) {
		// ask model to perform a DFS on vertex
		model.DFS( vertex );
		
		// display vertex name
		//viewer.displayVertexName(vertex);
		viewer.drawCircles( vertex );
		// display adjacency list
		viewer.displayAdjacencyList(vertex);
	}

}


Viewer


import java.util.ArrayList;
import processing.core.PApplet;

/**
 * The viewer class of a simple Model-View-Controller (MVC) system. The MVC
 * system displays a 50x50 network of vertices with left, right, up, and down
 * edges to their neighbors. This class is the main class, as it extends
 * PApplet, which will automatically call setup() on start-up, and will
 * repeatedly call draw(), 30 times a second. Constantly redraws the network of
 * vertices and edges held by the model, 30 times a second. Passes special
 * conditions to the controller for decision. For example, when the mouse is
 * over a vertex, or when the mouse is clicked somewhere on the canvas of the
 * applet.
 * 
 * @author D. Thiebaut
 *
 */

public class Lab15_Viewer extends PApplet {

	Lab15_Controller controller = null;
	Lab15_Model model = null;
	public int WIDTH = 800; // best generic width
	public int HEIGHT = 800; // best generic height
	private final int DELTAX = 10; // no pixels between vertices
	private final int DELTAY = 10; // no pixels between vertices
	private int MINDIST = 5; // min dist for vertex detected
	private int vertexUnderMouse = -1;
	private Button button1;

	// as under mouse pointer
	/**
	 * mutator: sets a reference to the model.
	 * 
	 * @param m
	 *            reference to the model
	 */
	public void setModel(Lab15_Model m) {
		model = m;
	}

	/**
	 * helper function. Given a vertex number, returns its position (x, y) on
	 * applet canvas. Vertex 0 will be at (DELTAX, DELTAY).
	 * 
	 * @param vertex
	 *            the vertex to position
	 * @param noRows
	 *            the number of rows in the network
	 * @param noCols
	 *            the number of columns in the network
	 * @return an int array. xy[0] is x, xy[1] is y. In pixels.
	 */
	private int[] getXY(int vertex, int noRows, int noCols) {
		int[] xy = new int[2];
		xy[0] = DELTAX + (int) (vertex / noRows) * DELTAX;
		xy[1] = DELTAY + (vertex % noRows) * DELTAY;
		return xy;
	}

	/**
	 * computes the number of vertex located at location (row, col).
	 * 
	 * @param row
	 * @param col
	 * @return the number (int) of the vertex.
	 */
	private int vertexAtRowCol(int row, int col) {
		int v = row + col * model.getNoRows();
		if (v < 0 || v > model.getNoVertices())
			return -1;
		return v;
	}

	/**
	 * the entry point for the MVC system. The PApplet will automatically call
	 * setup() first. This is the place that initializes everything up,
	 * including controller and model. Gets the controller to link the 3 systems
	 * together. The controller will initialize the model with the graph.
	 */
	public void setup() {
		// --- create model and controller, create reference ---
		// --- system between all three. ---
		controller = new Lab15_Controller();
		controller.initAll(this);

		// --- make window fit model graph exactly ---
		// --- add blank area of 30 pixels high at bottom ---
		// DELTAX = round( WIDTH / (model.getNoCols() + 1 ) );
		// DELTAY = round( HEIGHT / (model.getNoRows() + 1 ) );
		WIDTH = DELTAX * (model.getNoCols() + 1);
		HEIGHT = 30 + DELTAY * (model.getNoRows() + 1);

		// setup button1
		button1 = new Button(this, 10, HEIGHT - 25, "Magic Button");

		// --- set Applet geometry ---
		size(WIDTH, HEIGHT);
		smooth();
	}

	/**
	 * return the vertex that is within MINDIST pixels of the current mouse
	 * location.
	 * 
	 * @return the vertex number, or -1 if none found.
	 */
	public int closestVertexToMouse() {
		int row = round((mouseY - DELTAY) / DELTAY);
		int col = round((mouseX - DELTAX) / DELTAX);
		int v = vertexAtRowCol(row, col);
		if (v == -1)
			return -1;
		int[] xy = getXY(v, model.getNoRows(), model.getNoCols());
		if (dist(xy[0], xy[1], mouseX, mouseY) < MINDIST)
			return v;
		return -1;
	}

	/**
	 * draw() is called automatically 30 times a second by mechanism inside the
	 * PApplet that is hidden from the programmer. The role of draw() is to
	 * erase the screen (too fast for the user to notice) and redraw the graph
	 * with vertices and edges, and whatever text needs to be displayed on the
	 * canvas. Detects if the mouse pointer is over a vertex and if so, calls
	 * the controller to decide what should be done in this case.
	 */
	public void draw() {
		// erase window, make background white
		background(255, 255, 255);

		// draw button
		button1.draw();

		// get geometry of grid
		int V = model.getNoVertices();
		int noRows = model.getNoRows();
		int noCols = model.getNoCols();

		// if mouse just over a vertex, tell controller
		// so that specific action can be taken.
		vertexUnderMouse = closestVertexToMouse();
		if (vertexUnderMouse != -1)
			controller.setVertexUnderMouse(vertexUnderMouse);

		// draw larger colored circles for the vertices visited by the last DFS
		fill(255, 204, 0);       // orange interior color 
		stroke(255, 204, 0); // orange outline
		for (int v = 0; v < V; v++) {
			if (!model.isVisited(v))
				continue;

			// get coordinates of Vertex v
			int[] xy = getXY(v, noRows, noCols);

			// draw orange disk for Vertex v
			ellipse(xy[0], xy[1], 8, 8);
		}

		// draw the vertices and edges
		fill(0, 0, 0); // make vertices black
		stroke(0, 0, 0);
		strokeWeight(2); // make line width 2 pixels

		for (int v = 0; v < V; v++) {
			// get coordinates of Vertex v
			int[] xy = getXY(v, noRows, noCols);

			// draw black disk for Vertex v
			ellipse(xy[0], xy[1], 4, 4);

			// get adjacency list for each vertex and display edges
			if (button1.isON())
				for (int u : model.adjList(v)) {
					int[] xy2 = getXY(u, noRows, noCols);
					line(xy[0], xy[1], xy2[0], xy2[1]);
				}
		}
	}

	/**
	 * displays the vertex number next to the circle representing it. This is
	 * typically done whenever the mouse pointer is over a vertex.
	 * 
	 * @param vertexNo
	 */
	public void displayVertexName(int vertexNo) {
		// get position of vertex
		int[] xy = getXY(vertexNo, model.getNoRows(), model.getNoCols());
		// set text color to red
		fill(255, 0, 0);
		// display text
		text(" " + vertexNo, xy[0], xy[1]);
	}

	public void displayAdjacencyList(int vertexNo) {
		String s = "";
		for (int u : model.adjList(vertexNo))
			s += u + ", ";
		// remove last ", " part of the string
		s = s.trim();
		if (s.length() > 0)
			s = s.substring(0, s.length() - 1);
		text(s, 100, HEIGHT - 25);
	}

	static boolean upDown = true;
	static int radius = 0;

	public void drawCircles(int vertexNo) {
		int[] xy = getXY(vertexNo, model.getNoRows(), model.getNoCols());
		if (frameCount % 10 == 9)
			upDown = !upDown;

		if (upDown)
			radius = min(10, radius + 1);
		else
			radius = max(0, radius - 1);

		stroke(255, 0, 0);
		fill(255, 0, 0);
		ellipse(xy[0], xy[1], 2 * radius, 2 * radius);
	}

	public void mouseClicked() {
		// System.out.println( "Mouse clicked at "+mouseX + ", " + mouseY );
		if (button1.containsMouse()) {
			button1.switchState();
		}
	}
}