Math Home
Programming

Overview

In this lesson you will learn how to create the graph UI that is used in the graph theory math lessons.

The Graph



The Code

The only HTML element needed is a canvas to draw and interact with the graph. Since the nodes of the graph are draggable, disable the canvas from being selected. Here is the HTML for the canvas:

<canvas width='400px' height='400px' id='myCanvas' style='border: solid black 2px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;'></canvas>
		


Click the code or the description to see the connection.

Comments
JavaScript
>Set up variables for drawing on the canvas.
>Set up variables for the width and height of the canvas.
>Set up variables for the nodes and edges of the graph.
>Set up variables for storing coordinates of a selected node.
>Set up variables for storing the state of the user interaction. When no node is selected, the current node is set to -1.
>Create a circle function that will clear the canvas. This function will be used to update the image on the canvas.
>Create a clear function that draws circles. The circles are drawn at the point \((x,y)\) and their color depends on the state passed in.
>Create an init function that initializes the canvas and context variables and runs a draw function every 10 milliseconds. The draw function will update the drawing on the canvas.
>When the document loads, set up the variables as follows:
>>Set the width and height variable to the width and height of the canvas.
>>Create an array of nodes. Each node is an object with \((x,y)\) coordinates and a state.
>>Create an array of edges. An edge is 0 when two nodes are not connected and 1 when they are connected.
>>Run the init function to set up the canvas and iterate the draw function.
>>Create functions for clicking and releasing the mouse.
>The draw function will update the drawing on the canvas. It runs every 10 milliseconds.
>>First, clear the previous drawing.
>>Draw a circle for each node.
>>If edge[i][j] is 1, draw a line between node[i] and node[j]. This loop is sloppy because it considers both edge[i][j] and edge[j][i].
>>>This code draws the line between node[i] and node[j].
>Create a function that runs when the mouse is pressed.
>>Offset the x and y values relative to where the canvas is on the page. This way the top left corner of the canvas is \((0,0).\)
>>If no node is currently selected, do the following:
>>>Set the move function to myMove to allow nodes to be dragged.
>>>Loop through all the nodes to check whether they have been clicked. If so, set the state of the node to 1 and set startX and startY to the \((x,y)\) location of the node.
>>If a node is currently selected, do the following:
>>>Check for whether a node has been clicked.
>>>If the node that was selected was clicked again, remove all the edges by setting the edge array to 0 for that node.
>>>If a node that is different from the one that is selected was clicked, connect the two nodes by setting the edge array value to 1.
>>>After clicking, set the state of the node that was selected back to 0 and the state of the program back to unselected.
>Create a function for when the mouse is released.
>>Check the nodes for a node that was pressed (state == 1) and is where it started (by checking startX and startY).
>>>This node was clicked in place, so it has been selected. Set the state of the node to 2, the state of the program to selected, and the current node to this node.
>>If the node was not clicked in place, set its state to 0.
>>Set the mouse move function to null.
>Create a mouse move function for dragging nodes.
>>Offset the x and y values relative to where the canvas is on the page. This way the top left corner of the canvas is \((0,0).\)
>>If the \((x,y)\)-location of the mouse is in the canvas, find the node that is in state 1. This is the node that has been clicked.
>>>Update the coordinates of the clicked node to the coordinates of the mouse.
var canvas
var ctx
var WIDTH
var HEIGHT
var nodes = []
var edges = []
var startX
var startY
var state = {currState: "unselected", currNode: -1}
function clear() {
	ctx.clearRect(0,0,WIDTH,HEIGHT);
}
function circ(x,y,r,state) {
	if (state == 0) {
		ctx.fillStyle = '#000000';
	}
	else if (state == 1) {
		ctx.fillStyle = '#CCCCFF';
	}
	else if (state == 2) {
		ctx.fillStyle = '#0000FF';
	}
	else {
		ctx.fillStyle = '#AAAAAA';
	}
	  
	ctx.beginPath();
	ctx.arc(x,y,r,0,2*Math.PI);
	ctx.closePath();
	ctx.fill();
}
function init() {
	canvas = document.getElementById("myCanvas");
	ctx = canvas.getContext("2d");
	return setInterval(draw, 10);
}
window.onload = function() {
	WIDTH = 400;
	HEIGHT = 400;
	for (var i = 0; i < 10; i++) {
		nodes[i] = {x: 35*i+20, y: 20, r: 15, state: '0'}
	}
	for (var i = 0; i < nodes.length; i++) {
		edges[i] = []
		for (var j = 0; j < nodes.length; j++) {
			edges[i][j] = 0
		}
	}
	init();
	canvas.onmousedown = myDown;
	canvas.onmouseup = myUp;
}
function draw() {
	clear();
	for (var i = 0; i < nodes.length; i++) {
		circ(nodes[i].x, nodes[i].y, nodes[i].r, nodes[i].state)
	}
	for (var i = 0; i < nodes.length; i++) {
		for (var j = 0; j < nodes.length; j++) {
			if (edges[i][j] == 1) {
				ctx.beginPath()
				ctx.lineWidth = 3
				ctx.moveTo(nodes[i].x,nodes[i].y)
				ctx.lineTo(nodes[j].x,nodes[j].y)
				ctx.stroke()
				ctx.closePath()
			}
		}
	}
}
function myDown(e) {
    x = e.pageX - canvas.offsetLeft;
    y = e.pageY - canvas.offsetTop;
	if (state.currState == 'unselected') {
		canvas.onmousemove = myMove;
		for (var i = 0; i < nodes.length; i++) {
			if (nodes[i].x - nodes[i].r < x && x < nodes[i].x + nodes[i].r && nodes[i].y - nodes[i].r < y && y < nodes[i].y + nodes[i].r) {
				nodes[i].state = "1"
				startX = nodes[i].x
				startY = nodes[i].y
				break
			}
		}
	}
	else if (state.currState == 'selected') {
		for (var i = 0; i < nodes.length; i++) {
			if (nodes[i].x - nodes[i].r < x && x < nodes[i].x + nodes[i].r && nodes[i].y - nodes[i].r < y && y < nodes[i].y + nodes[i].r) {
				if (i == state.currNode) {
					for (var j = 0; j < nodes.length; j++) {
						edges[j][state.currNode] = 0
						edges[state.currNode][j] = 0
					}
				}
				else {
					edges[i][state.currNode] = 1
				}
			}
		}
		nodes[state.currNode].state = "0"
		state.currState = "unselected"
	}
}
function myUp() {
	for (var i = 0; i < nodes.length; i++) {
		if (nodes[i].state == 1) {
			if (nodes[i].x == startX && nodes[i].y == startY) {
				nodes[i].state = '2'
				state.currState = "selected"
				state.currNode = i
			}
			else {
				nodes[i].state = '0'
			}
		}
	}
	canvas.onmousemove = null;
}
function myMove(e) {
    x = e.pageX - canvas.offsetLeft;
    y = e.pageY - canvas.offsetTop;
	for (var i = 0; i < nodes.length; i++) {
		if (nodes[i].state == "1" && x < WIDTH && y < HEIGHT) {
			nodes[i].x = x
			nodes[i].y = y
		}
	}
}