434 行
14 KiB
JavaScript
434 行
14 KiB
JavaScript
// Copyright 2011 David Galles, University of San Francisco. All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other materials
|
|
// provided with the distribution.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
|
|
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// The views and conclusions contained in the software and documentation are those of the
|
|
// authors and should not be interpreted as representing official policies, either expressed
|
|
// or implied, of the University of San Francisco
|
|
|
|
|
|
var TABLE_ENTRY_WIDTH = 50;
|
|
var TABLE_ENTRY_HEIGHT = 25;
|
|
var TABLE_START_X = 50;
|
|
var TABLE_START_Y = 80;
|
|
|
|
var MESSAGE_LABEL_1_X = 20;
|
|
var MESSAGE_LABEL_1_Y = 10;
|
|
|
|
|
|
|
|
var HIGHLIGHT_CIRCLE_COLOR = "#000000";
|
|
|
|
|
|
|
|
function DijkstraPrim(am, runningDijkstra, w, h)
|
|
{
|
|
this.init(am, runningDijkstra, w, h);
|
|
|
|
}
|
|
|
|
DijkstraPrim.prototype = new Graph();
|
|
DijkstraPrim.prototype.constructor = DijkstraPrim;
|
|
DijkstraPrim.superclass = Graph.prototype;
|
|
|
|
DijkstraPrim.prototype.addControls = function()
|
|
{
|
|
addLabelToAlgorithmBar("Start Vertex: ");
|
|
this.startField = addControlToAlgorithmBar("Text", "");
|
|
this.startField.onkeydown = this.returnSubmit(this.startField, this.startCallback.bind(this), 2, true);
|
|
this.startField.size = 2
|
|
if (this.runningDijkstra)
|
|
{
|
|
this.startButton = addControlToAlgorithmBar("Button", "Run Dijkstra");
|
|
|
|
}
|
|
else
|
|
{
|
|
this.startButton = addControlToAlgorithmBar("Button", "Run Prim");
|
|
|
|
}
|
|
this.startButton.onclick = this.startCallback.bind(this);
|
|
DijkstraPrim.superclass.addControls.call(this, this.runningDijkstra);
|
|
}
|
|
|
|
|
|
DijkstraPrim.prototype.init = function(am, runningDijkstra, w, h)
|
|
{
|
|
this.runningDijkstra = runningDijkstra;
|
|
this.showEdgeCosts = true;
|
|
DijkstraPrim.superclass.init.call(this, am, w, h, false, false); // TODO: add no edge label flag to this?
|
|
// Setup called in base class init function
|
|
}
|
|
|
|
|
|
DijkstraPrim.prototype.setup = function()
|
|
{
|
|
this.message1ID = this.nextIndex++;
|
|
DijkstraPrim.superclass.setup.call(this);
|
|
|
|
this.commands = new Array();
|
|
this.cmd("CreateLabel", this.message1ID, "", MESSAGE_LABEL_1_X, MESSAGE_LABEL_1_Y, 0);
|
|
|
|
|
|
this.vertexID = new Array(this.size);
|
|
this.knownID = new Array(this.size);
|
|
this.distanceID = new Array(this.size);
|
|
this.pathID = new Array(this.size);
|
|
this.known = new Array(this.size);
|
|
this.distance = new Array(this.size);
|
|
this.path = new Array(this.size);
|
|
|
|
this.messageID = null;
|
|
|
|
for (var i = 0; i < this.size; i++)
|
|
{
|
|
this.vertexID[i] = this.nextIndex++;
|
|
this.knownID[i] = this.nextIndex++;
|
|
this.distanceID[i] = this.nextIndex++;
|
|
this.pathID[i] = this.nextIndex++;
|
|
this.cmd("CreateRectangle", this.vertexID[i], i, TABLE_ENTRY_WIDTH, TABLE_ENTRY_HEIGHT, TABLE_START_X, TABLE_START_Y + i*TABLE_ENTRY_HEIGHT);
|
|
this.cmd("CreateRectangle", this.knownID[i], "", TABLE_ENTRY_WIDTH, TABLE_ENTRY_HEIGHT, TABLE_START_X + TABLE_ENTRY_WIDTH, TABLE_START_Y + i*TABLE_ENTRY_HEIGHT);
|
|
this.cmd("CreateRectangle", this.distanceID[i], "", TABLE_ENTRY_WIDTH, TABLE_ENTRY_HEIGHT, TABLE_START_X + 2 * TABLE_ENTRY_WIDTH, TABLE_START_Y + i*TABLE_ENTRY_HEIGHT);
|
|
this.cmd("CreateRectangle", this.pathID[i], "", TABLE_ENTRY_WIDTH, TABLE_ENTRY_HEIGHT, TABLE_START_X+ 3 * TABLE_ENTRY_WIDTH, TABLE_START_Y + i*TABLE_ENTRY_HEIGHT);
|
|
this.cmd("SetTextColor", this.vertexID[i], VERTEX_INDEX_COLOR);
|
|
|
|
}
|
|
this.cmd("CreateLabel", this.nextIndex++, "Vertex", TABLE_START_X, TABLE_START_Y - TABLE_ENTRY_HEIGHT);
|
|
this.cmd("CreateLabel", this.nextIndex++, "Known", TABLE_START_X + TABLE_ENTRY_WIDTH, TABLE_START_Y - TABLE_ENTRY_HEIGHT);
|
|
this.cmd("CreateLabel", this.nextIndex++, "Cost", TABLE_START_X + 2 * TABLE_ENTRY_WIDTH, TABLE_START_Y - TABLE_ENTRY_HEIGHT);
|
|
this.cmd("CreateLabel", this.nextIndex++, "Path", TABLE_START_X + 3 * TABLE_ENTRY_WIDTH, TABLE_START_Y - TABLE_ENTRY_HEIGHT);
|
|
|
|
this.animationManager.setAllLayers([0, this.currentLayer]);
|
|
this.animationManager.StartNewAnimation(this.commands);
|
|
this.animationManager.skipForward();
|
|
this.animationManager.clearHistory();
|
|
this.comparisonMessageID = this.nextIndex++;
|
|
}
|
|
|
|
|
|
|
|
DijkstraPrim.prototype.findCheapestUnknown = function()
|
|
{
|
|
var bestIndex = -1;
|
|
this.cmd("SetText", this.message1ID,"Finding Cheapest Uknown Vertex");
|
|
|
|
for (var i = 0; i < this.size; i++)
|
|
{
|
|
if (!this.known[i])
|
|
{
|
|
this.cmd("SetHighlight", this.distanceID[i], 1);
|
|
}
|
|
|
|
if (!this.known[i] && this.distance[i] != -1 && (bestIndex == -1 ||
|
|
(this.distance[i] < this.distance[bestIndex])))
|
|
{
|
|
bestIndex = i;
|
|
}
|
|
}
|
|
if (bestIndex == -1)
|
|
{
|
|
var x = 3;
|
|
x = x + 2;
|
|
}
|
|
this.cmd("Step");
|
|
for (var i = 0; i < this.size; i++)
|
|
{
|
|
if (!this.known[i])
|
|
{
|
|
this.cmd("SetHighlight", this.distanceID[i], 0);
|
|
}
|
|
|
|
}
|
|
return bestIndex;
|
|
}
|
|
|
|
|
|
DijkstraPrim.prototype.doDijkstraPrim = function(startVertex)
|
|
{
|
|
this.commands = new Array();
|
|
|
|
if (!this.runningDijkstra)
|
|
{
|
|
this.recolorGraph();
|
|
}
|
|
|
|
|
|
var current = parseInt(startVertex);
|
|
|
|
for (var i = 0; i < this.size; i++)
|
|
{
|
|
this.known[i] = false;
|
|
this.distance[i] = -1;
|
|
this.path[i] = -1;
|
|
this.cmd("SetText", this.knownID[i], "F");
|
|
this.cmd("SetText", this.distanceID[i], "INF");
|
|
this.cmd("SetText", this.pathID[i], "-1");
|
|
this.cmd("SetTextColor", this.knownID[i], "#000000");
|
|
|
|
}
|
|
if (this.messageID != null)
|
|
{
|
|
for (i = 0; i < this.messageID.length; i++)
|
|
{
|
|
this.cmd("Delete", this.messageID[i]);
|
|
}
|
|
}
|
|
this.messageID = new Array();
|
|
|
|
this.distance[current] = 0;
|
|
this.cmd("SetText", this.distanceID[current], 0);
|
|
|
|
for (i = 0; i < this.size; i++)
|
|
{
|
|
current = this.findCheapestUnknown();
|
|
if (current < 0)
|
|
{
|
|
break;
|
|
}
|
|
this.cmd("SetText",this.message1ID, "Cheapest Unknown Vertex: " + current); // Gotta love Auto Conversion
|
|
this.cmd("SetHighlight", this.distanceID[current], 1);
|
|
|
|
this.cmd("SetHighlight", this.circleID[current], 1);
|
|
this.cmd("Step");
|
|
this.cmd("SetHighlight", this.distanceID[current], 0);
|
|
this.cmd("SetText", this.message1ID,"Setting known field to True");
|
|
this.cmd("SetHighlight", this.knownID[current], 1);
|
|
this.known[current] = true;
|
|
this.cmd("SetText", this.knownID[current], "T");
|
|
this.cmd("SetTextColor", this.knownID[current], "#AAAAAA");
|
|
this.cmd("Step");
|
|
this.cmd("SetHighlight", this.knownID[current], 0);
|
|
this.cmd("SetText",this.message1ID, "Updating neighbors of vertex " + current); // Gotta love Auto Conversion
|
|
for (var neighbor = 0; neighbor < this.size; neighbor++)
|
|
{
|
|
if (this.adj_matrix[current][neighbor] >= 0)
|
|
{
|
|
this.highlightEdge(current, neighbor, 1);
|
|
if (this.known[neighbor])
|
|
{
|
|
|
|
this.cmd("CreateLabel", this.comparisonMessageID,"Vertex " + String(neighbor) + " known",
|
|
TABLE_START_X + 5 * TABLE_ENTRY_WIDTH,TABLE_START_Y + neighbor*TABLE_ENTRY_HEIGHT);
|
|
this.cmd("SetHighlight", this.knownID[neighbor], 1);
|
|
}
|
|
else
|
|
{ this.cmd("SetHighlight", this.distanceID[current], 1);
|
|
this.cmd("SetHighlight", this.distanceID[neighbor], 1);
|
|
var distString = String(this.distance[neighbor]);
|
|
if (this.distance[neighbor] < 0)
|
|
{
|
|
distString = "INF";
|
|
}
|
|
|
|
if (this.runningDijkstra)
|
|
{
|
|
if (this.distance[neighbor] < 0 || this.distance[neighbor] > this.distance[current] + this.adj_matrix[current][neighbor])
|
|
{
|
|
this.cmd("CreateLabel", this.comparisonMessageID, distString + " > " + String(this.distance[current]) + " + " + String(this.adj_matrix[current][neighbor]),
|
|
TABLE_START_X + 4.3 * TABLE_ENTRY_WIDTH,TABLE_START_Y + neighbor*TABLE_ENTRY_HEIGHT);
|
|
}
|
|
else
|
|
{
|
|
this.cmd("CreateLabel", this.comparisonMessageID,"!(" + String(this.distance[neighbor]) + " > " + String(this.distance[current]) + " + " + String(this.adj_matrix[current][neighbor]) + ")",
|
|
TABLE_START_X + 4.3 * TABLE_ENTRY_WIDTH,TABLE_START_Y + neighbor*TABLE_ENTRY_HEIGHT);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (this.distance[neighbor] < 0 || this.distance[neighbor] > this.adj_matrix[current][neighbor])
|
|
{
|
|
this.cmd("CreateLabel", this.comparisonMessageID, distString + " > " + String(this.adj_matrix[current][neighbor]),
|
|
TABLE_START_X + 4.3 * TABLE_ENTRY_WIDTH,TABLE_START_Y + neighbor*TABLE_ENTRY_HEIGHT);
|
|
}
|
|
else
|
|
{
|
|
this.cmd("CreateLabel", this.comparisonMessageID,"!(" + String(this.distance[neighbor]) + " > " + String(this.adj_matrix[current][neighbor]) + ")",
|
|
TABLE_START_X + 4.3 * TABLE_ENTRY_WIDTH,TABLE_START_Y + neighbor*TABLE_ENTRY_HEIGHT);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
this.cmd("Step");
|
|
this.cmd("Delete", this.comparisonMessageID);
|
|
this.highlightEdge(current, neighbor, 0);
|
|
if (this.known[neighbor])
|
|
{
|
|
this.cmd("SetHighlight", this.knownID[neighbor], 0);
|
|
|
|
}
|
|
else
|
|
{
|
|
this.cmd("SetHighlight", this.distanceID[current], 0);
|
|
this.cmd("SetHighlight", this.distanceID[neighbor], 0);
|
|
var compare;
|
|
if (this.runningDijkstra)
|
|
{
|
|
compare = this.distance[current] + this.adj_matrix[current][neighbor];
|
|
}
|
|
else
|
|
{
|
|
compare = this.adj_matrix[current][neighbor];
|
|
}
|
|
if (this.distance[neighbor] < 0 || this.distance[neighbor] > compare)
|
|
{
|
|
this.distance[neighbor] = compare;
|
|
this.path[neighbor] = current;
|
|
this.cmd("SetText", this.distanceID[neighbor],this.distance[neighbor] );
|
|
this.cmd("SetText", this.pathID[neighbor],this.path[neighbor]);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
this.cmd("SetHighlight", this.circleID[current], 0);
|
|
|
|
}
|
|
// Running Dijkstra's algorithm, create the paths
|
|
if (this.runningDijkstra)
|
|
{
|
|
this.cmd("SetText",this.message1ID, "Finding Paths in Table");
|
|
this.createPaths();
|
|
}
|
|
// Running Prim's algorithm, highlight the tree
|
|
else
|
|
{
|
|
this.cmd("SetText",this.message1ID, "Creating tree from table");
|
|
this.highlightTree();
|
|
}
|
|
|
|
this.cmd("SetText",this.message1ID, "");
|
|
return this.commands
|
|
}
|
|
|
|
DijkstraPrim.prototype.createPaths = function()
|
|
{
|
|
for (var vertex = 0; vertex < this.size; vertex++)
|
|
{
|
|
var nextLabelID = this.nextIndex++;
|
|
if (this.distance[vertex] < 0)
|
|
{
|
|
this.cmd("CreateLabel", nextLabelID, "No Path", TABLE_START_X + 4.3 * TABLE_ENTRY_WIDTH,TABLE_START_Y + vertex*TABLE_ENTRY_HEIGHT);
|
|
this.messageID.push(nextLabelID);
|
|
}
|
|
else
|
|
{
|
|
this.cmd("CreateLabel", nextLabelID, vertex, TABLE_START_X + 4.3 * TABLE_ENTRY_WIDTH,TABLE_START_Y + vertex*TABLE_ENTRY_HEIGHT);
|
|
this.messageID.push(nextLabelID);
|
|
var pathList = [nextLabelID];
|
|
var nextInPath = vertex;
|
|
while (nextInPath >= 0)
|
|
{
|
|
this.cmd("SetHighlight", this.pathID[nextInPath], 1);
|
|
this.cmd("Step");
|
|
if (this.path[nextInPath] != -1)
|
|
{
|
|
nextLabelID = this.nextIndex++;
|
|
this.cmd("CreateLabel", nextLabelID, this.path[nextInPath], TABLE_START_X + 3 * TABLE_ENTRY_WIDTH,TABLE_START_Y + nextInPath*TABLE_ENTRY_HEIGHT);
|
|
this.cmd("Move", nextLabelID, TABLE_START_X + 4.3 * TABLE_ENTRY_WIDTH,TABLE_START_Y + vertex*TABLE_ENTRY_HEIGHT);
|
|
this.messageID.push(nextLabelID);
|
|
for (var i = pathList.length - 1; i >= 0; i--)
|
|
{
|
|
this.cmd("Move", pathList[i], TABLE_START_X + 4.3 * TABLE_ENTRY_WIDTH + (pathList.length - i) * 17,TABLE_START_Y + vertex*TABLE_ENTRY_HEIGHT)
|
|
|
|
}
|
|
this.cmd("Step");
|
|
pathList.push(nextLabelID);
|
|
}
|
|
this.cmd("SetHighlight", this.pathID[nextInPath], 0);
|
|
nextInPath = this.path[nextInPath];
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DijkstraPrim.prototype.highlightTree = function()
|
|
{
|
|
for (var vertex = 0; vertex < this.size; vertex++)
|
|
{
|
|
if (this.path[vertex] >= 0)
|
|
{
|
|
this.cmd("SetHighlight", this.vertexID[vertex], 1)
|
|
this.cmd("SetHighlight", this.pathID[vertex], 1);
|
|
this.highlightEdge(vertex, this.path[vertex], 1)
|
|
this.highlightEdge(this.path[vertex], vertex, 1)
|
|
this.cmd("Step");
|
|
this.cmd("SetHighlight", this.vertexID[vertex], 0)
|
|
this.cmd("SetHighlight", this.pathID[vertex], 0);
|
|
this.highlightEdge(vertex, this.path[vertex], 0)
|
|
this.highlightEdge(this.path[vertex], vertex, 0)
|
|
this.setEdgeColor(vertex, this.path[vertex], "#FF0000");
|
|
this.setEdgeColor(this.path[vertex], vertex,"#FF0000");
|
|
}
|
|
}
|
|
}
|
|
|
|
DijkstraPrim.prototype.reset = function()
|
|
{
|
|
this.messageID = new Array();
|
|
}
|
|
|
|
DijkstraPrim.prototype.startCallback = function(event)
|
|
{
|
|
var startValue;
|
|
|
|
if (this.startField.value != "")
|
|
{
|
|
startvalue = this.startField.value;
|
|
this.startField.value = "";
|
|
if (parseInt(startvalue) < this.size)
|
|
this.implementAction(this.doDijkstraPrim.bind(this),startvalue);
|
|
}
|
|
}
|
|
|
|
|
|
DijkstraPrim.prototype.enableUI = function(event)
|
|
{
|
|
this.startField.disabled = false;
|
|
this.startButton.disabled = false;
|
|
this.startButton
|
|
|
|
|
|
DijkstraPrim.superclass.enableUI.call(this,event);
|
|
}
|
|
DijkstraPrim.prototype.disableUI = function(event)
|
|
{
|
|
|
|
this.startField.disabled = true;
|
|
this.startButton.disabled = true;
|
|
|
|
DijkstraPrim.superclass.disableUI.call(this, event);
|
|
}
|
|
|
|
|
|
var currentAlg;
|
|
|
|
function init(runDijkstra)
|
|
{
|
|
var animManag = initCanvas();
|
|
currentAlg = new DijkstraPrim(animManag, runDijkstra, canvas.width, canvas.height);
|
|
}
|