<!doctype html>
<html>
<head>
<title>Animation Demo</title>
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
body {
background-color: #FFFFFF;
}
#simProgress {
width: 378px;
height: 300px;
font-size: 0.8em;
}
#simClock {
width: 384px;
height: 30px;
background-color: #EEEEEE;
}
</style>
</head>
<body>
<div id="wrapper">
<canvas id="bcanimation" height="250px" width="380px">The HTML5 Canvas object is not supported on your browser.</canvas>
<br />
<button id="step" onmouseup="stepClick()" style="height: 50px; width: 384px">Step</button>
</div>
<!--p id="stuff"></p-->
<hr />
<div id="simClock"></div>
<textarea id="simProgress"></textarea>
<script>
/* if (!Array.prototype.forEach)
{
console.log("array prototype needed to be defined");
Array.prototype.forEach = function(fun /*, thisp*/ /*)
{
var len = this.length;
if (typeof fun != "function")
throw new TypeError();
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in this)
fun.call(thisp, this[i], i, this);
}
};
}
*/
//***************************************************************************************
//*** utility and statistical functions for Discrete-Event Simulation Framework
//***************************************************************************************
function factorial(inputValue) {
var input = Math.floor(inputValue);
if (input < 0) {
return -1; //error condition
} else if (input <= 1) {
return 1;
} else {
var result = 1;
for (i=2; i<=input; i++) {
result *= i;
}
}
return result;
}; //factorial
function poissonSingle(arrivalsPerInterval,interval,occurrences) {
//sanity check the input
if (arrivalsPerInterval <= 0.0) {arrivalsPerInterval = 1.0;}
if (interval <= 0.0) {interval = 1.0;}
if (occurrences <= 0) {occurrences = 0;}
/* //the clear way
var lambda = arrivalsPerInterval / interval;
var k = occurrences;
var lambda_K = Math.pow(lambda,k);
var e_minusLambda = Math.exp(-lambda);
var kFact = factorial(k);
var result = lambda_K * e_minusLambda / kFact;
return result; */
//the faster way
lambda = arrivalsPerInterval / interval;
return ((Math.pow(lambda,occurrences)) * (Math.exp(-lambda)) / factorial(occurrences));
} //poissonSingle
function poissonCumulative(arrivalsPerInterval,interval) {
//both numbers should be positive, do not need to be integers
if (arrivalsPerInterval <= 0.0) {arrivalsPerInterval = 1.0;}
if (interval <= 0.0) {interval = 1.0;}
var cumulative = 0.0;
var count = 0;
var cumulations = [];
while (cumulative < 0.9999999999) {
var next = poissonSingle(arrivalsPerInterval,interval,count);
cumulative += next;
cumulations[count] = cumulative;
count++;
}
cumulations[count] = 1.0;
return cumulations;
} //poissonCumulative
function poissonArrivals(randomValue,cumulation) {
//expect 0.0 <= randomValue < 1.0, hack to condition input
var input = randomValue;
if (input < 0.0) {
input = 0.0;
} else if (input >= 1.0) {
input = 0.9999999999;
}
var index = 0;
while (cumulation[index] < input) {
index++;
}
return index;
}
/* var c = poissonCumulative(3,1);
var r = 0.999999999999999;
var a = poissonArrivals(r,c);
alert(a); //result is 20
r = 0.5;
a = poissonArrivals(r,c);
alert(a); //result is 3
r = Math.random();
a = poissonArrivals(r,c);
alert(a); //result is ?
*/
// alert(poissonCumulative(6,1));
/* alert(poissonSingle(6,1,0));
alert(poissonSingle(6,1,1));
alert(poissonSingle(6,1,2));
alert(poissonSingle(6,1,3));
alert(poissonSingle(6,1,4));
alert(poissonSingle(6,1,5));
alert(poissonSingle(6,1,6));
alert(poissonSingle(6,1,7));
alert(poissonSingle(6,1,8));
alert(poissonSingle(6,1,9));
*/
//***************************************************************************************
//*** some global variables (Ick, I know...)
//***************************************************************************************
var displayStartTime = 0.0;
var displayCurrentTime = 0.0;
var displayEndTime = 0.0;
var displayEntitiesScheduled = 0;
var displayEntitiesGenerated = 0;
var displayEntityID = 0;
var initBallFlag = false;
var displayActivity = "";
//***************************************************************************************
//*** start Discrete-Event Simulation Framework
//***************************************************************************************
//global simulation clock
var globalSimClock = 0.0; //starts at zero, units to be specified
var globalSimUnits = "minutes";
var globalInsertTime = 0.0; //TODO: review whether this is needed or useful
var simClockElement = document.getElementById("simClock");
function updateSimClock(time) {
globalSimClock = time;
simClockElement.innerHTML = time.toFixed(6) + " " + globalSimUnits;
};
updateSimClock(0.0);
var simProgressText = "";
var simProgressElement = document.getElementById("simProgress");
function displayProgressText(textAdd) {
simProgressText += textAdd + "\n"; //+ "<br />";
simProgressElement.innerHTML = simProgressText;
simProgressElement.scrollTop = simProgressElement.scrollHeight;
};
function clearProgressText() {
simProgressText = "";
simProgressElement.innerHTML = simProgressText;
};
clearProgressText();
var globalIDCounter = 0;
function getNewID() {
return ++globalIDCounter;
};
var globalExecutionCount = 0;
function bumpGlobalExecutionCount() {
globalExecutionCount++;
}
//current events queue item
function currentEventItem(entity) {
this.entity = entity;
this.checkBlockingCondition = function() {
return this.entity.checkBlockingCondition(); //true if blocking condition met
}
}; //currentEventItem
//the current events queue definition, a list of current events queue items
function currentEventsQueue() {
this.ceq = new Array();
this.ceqSize = 0;
this.addCeqItem = function(ceqItem) {
this.ceq.push(ceqItem);
this.ceqSize++;
}
//this.removeCeqItem = function(ceqItem) {
// var index = this.ceq.indexOf(ceqItem);
// this.ceq.splice(index,1);
// this.ceqSize--;
//}
this.processCeq = function() {
var trueBlockFound;
do {
var i = 0;
trueBlockFound = false;
while ((i < this.ceqSize) && (!trueBlockFound)) {
if (this.ceq[i].checkBlockingCondition()) {
this.ceq.splice(i,1);
this.ceqSize--;
trueBlockFound = true;
} else {
i++;
}
}
} while (trueBlockFound);
}
};
//the current events queue instantiation
ceq = new currentEventsQueue();
//future events queue item
function futureEventItem(time,type,entity) {
this.activationTime = 0.0;
if (type == "advance") {
this.activationTime = globalSimClock + time; //activate 'time' from now
} else if (type == "absolute") {
if (time >= globalSimClock) {
this.activationTime = time; //activate at specified absolute time, if in future
} else {
displayProgressText("Invalid time past time supplied A. Entity ID: "+entity.entityID+" Current Time: "+globalSimClock+" Specified Time: "+time); //alert on invalid time
//alert("Invalid time past time supplied. Entity ID: "+entity.entityID+" Current Time: "+globalSimClock+" Speficied Time: "+time); //alert on invalid time
}
}
this.entity = entity;
this.getActivationTime = function() {
return this.activationTime;
};
this.update = function(time,type) {
if (type == "advance") {
this.activationTime = globalSimClock + time; //activate 'time' from now
} else if (type == "absolute") {
if (time >= globalSimClock) {
this.activationTime = time; //activate at specified absolute time, if in future
} else {
displayProgressText("Invalid time past time supplied B. Entity ID: "+entity.entityID+" Current Time: "+globalSimClock+" Specified Time: "+time); //alert on invalid time
//alert("Invalid time past time supplied. Entity ID: "+entity.entityID+" Current Time: "+globalSimClock+" Speficied Time: "+time); //alert on invalid time
}
}
};
this.reportItem = function() {
console.log("Time: "+this.activationTime+" ID: "+this.entity.entityID);
};
}; //futureEventItem
var feqCurrent; //global feqItem
function futureEventsQueue() {
this.feq = new Array();
this.feqSize = 0;
this.insertTime = 0.0;
this.findLaterTime = function(item) {
//updateSimClock(10.0);
var a = item.getActivationTime();
var b = globalInsertTime; //this.insertTime; why is the scope of "this" messed up here? window and not queue
var result = a > b;
return result;
};
this.insertItem = function(time,type,entity) {
//create futureEventItem
var feqItem = new futureEventItem(time,type,entity);
this.insertTime = feqItem.getActivationTime();
globalInsertTime = this.insertTime;
if (this.feqSize == 0) {
this.feq[0] = feqItem;
this.feqSize++;
///console.log("Array size: "+this.feq.length);
} else {
//find index of feq item to insert before
var insertIndex = this.feq.findIndex(this.findLaterTime);
//insert the element
if (insertIndex < 0) {
insertIndex = this.feq.length;
}
this.feq.splice(insertIndex,0,feqItem);
this.feqSize++;
///console.log("Array size: "+this.feq.length);
}
};
this.newItem = function(time,entity) {
//create futureEventItem
var feqItem = new futureEventItem(time,"absolute",entity);
this.insertTime = feqItem.getActivationTime();
globalInsertTime = this.insertTime;
if (this.feqSize == 0) {
this.feq[0] = feqItem;
this.feqSize++;
///console.log("Array size: "+this.feq.length);
} else {
//find index of feq item to insert before
var insertIndex = this.feq.findIndex(this.findLaterTime);
//insert the element
if (insertIndex < 0) {
insertIndex = this.feq.length;
}
this.feq.splice(insertIndex,0,feqItem);
this.feqSize++;
///console.log("Array size: "+this.feq.length);
}
};
this.getFirstItem = function() {
var feqItem = this.feq.splice(0,1);
if (feqItem.length) {
var t = feqItem[0].getActivationTime();
updateSimClock(t);
this.feqSize--;
return feqItem;
} else {
console.log("no items in FEQ to retrieve "+this.feqSize)
}
};
this.insertExistingItem = function(feqItem) {
this.insertTime = feqItem.getActivationTime();
globalInsertTime = this.insertTime;
if (this.feqSize == 0) {
this.feq[0] = feqItem;
this.feqSize++;
///console.log("Array size: "+this.feq.length);
} else {
//find index of feq item to insert before
var insertIndex = this.feq.findIndex(this.findLaterTime);
//insert the element
if (insertIndex < 0) {
insertIndex = this.feq.length;
}
this.feq.splice(insertIndex,0,feqItem);
this.feqSize++;
///console.log("Array size: "+this.feq.length);
}
};
this.insertCurrent = function() {
this.insertTime = feqCurrent.getActivationTime();
globalInsertTime = this.insertTime;
if (this.feqSize == 0) {
this.feq[0] = feqCurrent;
this.feqSize++;
///console.log("Array size: "+this.feq.length);
} else {
//find index of feq item to insert before
var insertIndex = this.feq.findIndex(this.findLaterTime);
//insert the element
if (insertIndex < 0) {
insertIndex = this.feq.length;
}
this.feq.splice(insertIndex,0,feqCurrent);
this.feqSize++;
///console.log("Array size: "+this.feq.length);
}
};
this.reportSize = function() {
///console.log("Number of events in queue: "+this.feqSize);
};
}; //futureEventsQueue
function reportItemInfo(element, index, arr) {
//globalSimClock = 10.0;
console.log("reportItemInfo: element: "+element+" index: "+index+" arr: "+arr);
element.reportItem();
};
//initialize future events queue
feq = new futureEventsQueue();
feq.reportSize(); //should be zero;
//function advance(feqItem,byTime) {
function advance(byTime) {
feqCurrent.update(byTime,"advance");
//feq.insertExistingItem(feqCurrent);
feq.insertCurrent();
};
//entity item 1
function entity1(initialTime,incrementTime,endTime) {
this.entityID = getNewID();
this.initialTime = initialTime;
this.incrementTime = incrementTime;
this.endTime = endTime;
this.nextState = "increment";
feq.newItem(initialTime,this);
displayProgressText("entity "+this.entityID+" created at time "+globalSimClock);
this.activate = function() {
bumpGlobalExecutionCount();
if (this.nextState == "increment") {
displayProgressText("entity "+this.entityID+" updated at time "+globalSimClock);
//set values for diplay-------------
//displayStartTime = globalSimClock;
displayCurrentTime = globalSimClock;
//displayEndTime = globalSimClock + this.incrementTime;
//displayEntitiesScheduled = arrivals;
displayEntitiesGenerated++;
displayEntityID = this.entityID;
initBallFlag = true;
displayActivity = "new entity";
//----------------------------------
if (globalSimClock + this.incrementTime >= this.endTime) {
this.nextState = "destroy";
}
advance(this.incrementTime);
} else if (this.nextState == "destroy") {
displayProgressText("entity "+this.entityID+" terminated at time "+globalSimClock);
//set values for diplay-------------
//displayStartTime = globalSimClock;
displayCurrentTime = globalSimClock;
//displayEndTime = globalSimClock + this.incrementTime;
//displayEntitiesScheduled = arrivals;
//displayEntitiesGenerated++;
displayEntityID = this.entityID;
initBallFlag = false;
displayActivity = "terminated";
//----------------------------------
} else {
alert("entity "+this.entityID+" went into undefined state");
displayProgressText("entity "+this.entityID+" in undefined state at time "+globalSimClock);
}
}; //this.activate
}; //entity
function errorUndefinedAdvanceState(id,state) {
//alert("entity "+id+" went into undefined state");
displayProgressText("entity "+id+" in undefined state: "+state+", at time "+globalSimClock);
}
//entity item 2
function entity2(initialTime,initialPosition,speed,endPosition) {
this.entityID = getNewID();
this.initialTime = initialTime;
this.currentPosition = initialPosition;
this.speed = speed;
this.endPosition = endPosition;
this.nextState = "go_forward";
this.waitCountdown = 5;
feq.newItem(initialTime,this);
displayProgressText("entity "+this.entityID+" created at time "+globalSimClock);
this.go_forward = function() {
displayProgressText("entity "+this.entityID+" updated at time "+globalSimClock+" position: "+this.currentPosition);
this.currentPosition += this.speed;
if (this.currentPosition >= this.endPosition) {
if (this.currentPosition > this.endPosition) {
this.currentPosition = this.endPosition;
}
this.nextState = "wait";
}
advance(7.01);
}
this.wait = function() {
this.waitCountdown--;
displayProgressText("entity "+this.entityID+" waiting at time "+globalSimClock+" wait count: "+this.waitCountdown);
if (this.waitCountdown <= 0.0) {
this.nextState = "destroy";
}
advance(7.01);
}
this.destroy = function() {
displayProgressText("entity "+this.entityID+" terminated at time "+globalSimClock);
}
this.activate = function() {
bumpGlobalExecutionCount();
if (this.nextState == "go_forward") {
this.go_forward();
} else if (this.nextState == "wait") {
this.wait();
} else if (this.nextState == "destroy") {
this.destroy();
} else {
errorUndefinedAdvanceState(this.entityID,this.nextState);
}
}; //this.activate
}; //entity2
//entity item 2a
function entity2a(initialTime,initialPosition,speed,endPosition) {
var go_forward_ = 0;
var wait_ = 1; //define named index values for clarity
var destroy_ = 2;
this.entityID = getNewID();
this.initialTime = initialTime;
this.currentPosition = initialPosition;
this.speed = speed;
this.endPosition = endPosition;
this.nextState = go_forward_;
this.waitCountdown = 5;
feq.newItem(initialTime,this);
displayProgressText("entity "+this.entityID+" created at time "+globalSimClock);
this.go_forward = function(entity) {
displayProgressText("entity "+this.entityID+" updated at time "+globalSimClock+" position: "+this.currentPosition);
this.currentPosition += this.speed;
if (this.currentPosition >= this.endPosition) {
if (this.currentPosition > this.endPosition) {
this.currentPosition = this.endPosition;
}
this.nextState = wait_;
}
advance(7.01);
}
this.wait = function() {
this.waitCountdown--;
displayProgressText("entity "+this.entityID+" waiting at time "+globalSimClock+" wait count: "+this.waitCountdown);
if (this.waitCountdown <= 0.0) {
this.nextState = destroy_;
}
advance(7.01);
}
this.destroy = function() {
displayProgressText("entity "+this.entityID+" terminated at time "+globalSimClock);
}
//define the array of functions here
this.activateArray = [this.go_forward,this.wait,this.destroy];
this.activate = function() {
bumpGlobalExecutionCount();
if ((this.nextState >= 0) && (this.nextState <= 2)) {
//call the desired function this way
this.activateArray[this.nextState]();
} else {
errorUndefinedAdvanceState(this.entityID,this.nextState);
}
}; //this.activate
}; //entity2a
//entity item 3
function entity3(initialTime,executionCount) {
this.entityID = getNewID();
this.initialTime = initialTime;
this.executionCount = executionCount;
//this.nextState = "block1";
//feq.newItem(initialTime,this);
this.ceqItem = new currentEventItem(this);
ceq.addCeqItem(this.ceqItem);
displayProgressText("entity "+this.entityID+" created at time "+globalSimClock);
this.blockingCondition = 0;
this.checkBlockingCondition = function() {
if (this.blockingCondition < 0) {
return false;
} else if (this.blockingCondition == 0) {
var fred = globalExecutionCount % this.executionCount;
if (Math.abs(fred) <= 0.00001) {
displayProgressText("entity "+this.entityID+" reports at time "+globalSimClock+" executions: "+globalExecutionCount);
return true;
} else {
return false;
}
}
}
/* this.activate = function() {
if (this.nextState == "block1") {
displayProgressText("entity "+this.entityID+" checking blocks at time "+globalSimClock+" blockState: "+this.blockingCondition);
ceq.addItem(this.ceqItem);
} else {
alert("entity "+this.entityID+" went into undefined state");
displayProgressText("entity "+this.entityID+" in undefined state at time "+globalSimClock);
}
}; //this.activate */
}; //entity3
var conditionFlag = function(flag) {
this.flag = flag;
this.getFlag = function () {
return this.flag;
}
this.setFlag = function(flag) {
this.flag = flag;
}
}
var flag1 = new conditionFlag(false);
var flag2 = new conditionFlag(false);
//entity item 4
function entity4(initialTime,incrementTime,endTime,flagPointer) {
this.entityID = getNewID();
this.initialTime = initialTime;
this.incrementTime = incrementTime;
this.endTime = endTime;
this.flagPointer = flagPointer;
this.nextState = "increment";
feq.newItem(initialTime,this);
displayProgressText("entity "+this.entityID+" created at time "+globalSimClock);
this.activate = function() {
bumpGlobalExecutionCount();
if (this.nextState == "increment") {
displayProgressText("entity "+this.entityID+" sets flag at time "+globalSimClock+" flag value: "+this.flagPointer.getFlag()+" flag1: "+flag1.getFlag()+" flag2: "+flag2.getFlag());
if (globalSimClock + this.incrementTime >= this.endTime) {
this.nextState = "destroy";
}
advance(this.incrementTime);
} else if (this.nextState == "destroy") {
this.flagPointer.setFlag(true);
displayProgressText("entity "+this.entityID+" terminated at time "+globalSimClock+" flag value: "+this.flagPointer.getFlag()+" flag1: "+flag1.getFlag()+" flag2: "+flag2.getFlag());
} else {
alert("entity "+this.entityID+" went into undefined state");
displayProgressText("entity "+this.entityID+" in undefined state at time "+globalSimClock);
}
}; //this.activate
}; //entity
//entity item 5
function entity5(initialTime,flagPointer) {
this.entityID = getNewID();
this.initialTime = initialTime;
this.flagPointer = flagPointer;
//this.nextState = "block1";
//feq.newItem(initialTime,this);
this.ceqItem = new currentEventItem(this);
ceq.addCeqItem(this.ceqItem);
displayProgressText("entity "+this.entityID+" created at time "+globalSimClock);
this.blockingCondition = 0;
this.checkBlockingCondition = function() {
if (this.blockingCondition < 0) {
return false;
} else if (this.blockingCondition == 0) {
if (this.flagPointer.getFlag()) {
displayProgressText("entity "+this.entityID+" unblocks at time "+globalSimClock+" flag value: "+this.flagPointer.getFlag());
return true;
} else {
displayProgressText("entity "+this.entityID+" still blocked at time "+globalSimClock+" flag value: "+this.flagPointer.getFlag()+" flag1: "+flag1.getFlag()+" flag2: "+flag2.getFlag());
return false;
}
}
}
}; //entity5
//time to end the entire simulation
var endSimTime = 360.0; //time increments in minutes, run for 6 hours
//collection of all entities in the system
var setOfEntities = new Array();
function compareNumeric(a,b) {
return a-b;
}
//Entry component
function entryComponent(scheduleBlockMinutes,scheduleArray) {
//initially assume it just generates entity1s that disappear after a single cycle
this.entityID = getNewID();
this.initialTime = 0.0;
this.incrementTime = scheduleBlockMinutes;
this.arrayIndex = 0;
this.endTime = endSimTime;
this.nextState = "increment";
feq.newItem(globalSimClock,this); //assume all components created at time zero
this.generateNewEntity = function(start,increment,end) {
var newEntity = new entity1(start,increment,end);
setOfEntities.push(newEntity);
}
this.increment = function() { //should be called at the beginning of every half-hour block
//get arrival count per array index
var arrivals = scheduleArray[this.arrayIndex];
if (arrivals > 0) {
/* //distribute arrivals evenly across the time span
var increment = scheduleBlockMinutes / (arrivals + 1);
var arrivalBump = 0.0;
for (var i=0; i<arrivals; i++) {
arrivalBump += increment;
this.generateNewEntity(globalSimClock + arrivalBump, 2.0, globalSimClock + arrivalBump + 1.0);
} */
//distribute arrivals randomly across the time span
/* for (var i=0; i<arrivals; i++) {
var arrivalBump = Math.random() * scheduleBlockMinutes;
this.generateNewEntity(globalSimClock + arrivalBump, 2.0, globalSimClock + arrivalBump + 1.0);
} */
//distribute arrivals randomly across the time span but have them arrive in sorted order by ID
var arrivalArray = [];
for (var i=0; i<arrivals; i++) {
arrivalArray[i] = Math.random() * scheduleBlockMinutes;
}
//sort the array
arrivalArray.sort(compareNumeric);
//generate the arrivals
for (var i=0; i<arrivals; i++) {
var arrivalBump = arrivalArray[i];
this.generateNewEntity(globalSimClock + arrivalBump, 2.0, globalSimClock + arrivalBump + 1.0);
}
}
this.arrayIndex++;
displayProgressText("Entry component "+this.entityID+" generates "+arrivals+" new entities at time "+globalSimClock);
//set values for diplay-------------
displayStartTime = globalSimClock;
displayCurrentTime = globalSimClock;
displayEndTime = globalSimClock + this.incrementTime;
displayEntitiesScheduled = arrivals;
displayEntitiesGenerated = 0;
displayEntityID = this.entityID;
displayActivity = "Entry update";
//----------------------------------
if (globalSimClock + this.incrementTime >= this.endTime) {
this.nextState = "destroy";
}
advance(this.incrementTime);
}
this.destroy = function() {
//this.flagPointer.setFlag(true);
displayProgressText("Entry component "+this.entityID+" terminated at time "+globalSimClock);
}
this.activate = function() {
if (this.nextState == "increment") {
this.increment();
} else if (this.nextState == "destroy") {
this.destroy();
} else {
errorUndefinedAdvanceState(this.entityID,this.nextState);
}
}
}; //entryComponent
//schedule for six hours of arrivals in half-hour blocks
var arrivalSchedule = [0,0,1,3,4,4,5,6,3,2,1,0];
var entry1 = new entryComponent(30.0,arrivalSchedule);
/* var entityA = new entity1(11.0,10.0,100.0);
var entityB = new entity1(13.0,13.0,100.0);
var entityC = new entity2a(12.0,-40,5,-1);
var entityD = new entity3(0.0,5);
var entityE = new entity4(0.0,45.0,45.0,flag1);
var entityF = new entity4(0.0,63.7,63.7,flag2);
var entityG = new entity5(0.0,flag1);
var entityH = new entity5(0.0,flag2);
*/
//***** main event loop *************************************************************
var keepRunning = true;
/* while (keepRunning) {
ceq.processCeq();
var itemList = feq.getFirstItem();
if (itemList) {
feqCurrent = itemList[0];
feqCurrent.entity.activate();
} else {
keepRunning = false;
}
} //while (keepRunning)
*/
//***************************************************************************************
//*** end Discrete-Event Simulation Framework
//***************************************************************************************
//***************************************************************************************
//*** begin animation section
//***************************************************************************************
//handle to animation mechanism
var requestID;
//status flag
var running = false;
//animation object
function Animation(width,height,id) {
this.id = id;
this.xDemo = 0.0;
this.yDemo = 0.0;
this.yVelocity = 0.0;
this.canvas = document.getElementById(id);
if (this.canvas) {
//element already exists
if (this.canvas.nodeName == "CANVAS") {
//element *is* a canvas
this.width = this.canvas.width;
this.height = this.canvas.height;
} else {
//element is *not* a canvas
alert("Error: Element you've linked to is *not* a canvas.");
}
} else {
//element does not already exist and must be created
this.width = width;
this.height = height;
this.canvas = document.createElement("canvas");
this.canvas.id = this.id;
this.canvas.width = this.width;
this.canvas.height = this.height;
document.body.appendChild(this.canvas);
}
this.canvas.style.border = "2px solid lime";
this.canvas.style.margin = "5px auto";
this.ctx = this.canvas.getContext("2d");
this.ctx.lineWidth = 1.0;
this.ctx.lineCap = "butt"; //"square";
this.clearCanvas = function(color) {
this.ctx.fillStyle = color;
this.ctx.fillRect(0,0,this.width,this.height);
}
this.drawCircle = function(x,y,r,c) {
this.ctx.strokeStyle = c;
this.ctx.beginPath();
this.ctx.arc(x,y,r,0,2*Math.PI,true);
this.ctx.stroke();
this.ctx.fillStyle = c;
this.ctx.fill();
};
this.drawEntry = function(x,y,w,h,c) {
this.xLocation = x;
this.yLocation = y;
this.entryWidth = w;
this.entryHeight = h;
this.color = c;
this.clearCanvas("#000000");
this.ctx.strokeStyle = c;
this.ctx.beginPath();
this.ctx.moveTo(x+0.5,y+0.5);
this.ctx.lineTo(x+w+0.5,y+0.5);
this.ctx.lineTo(x+w+0.5,y+h+0.5);
this.ctx.lineTo(x+0.5,y+h+0.5);
this.ctx.lineTo(x+0.5,y+0.5);
this.ctx.stroke();
this.ctx.font = "12px Arial";
this.ctx.fillStyle = "#00FFFF";
this.ctx.textAlign = "left";
this.ctx.fillText("Entry",x+85,y+12);
this.ctx.textAlign = "right";
this.ctx.fillText("Start Time:",x+78,y+24);
this.ctx.fillText("Current Time:",x+78,y+36);
this.ctx.fillText("End Time:",x+78,y+48);
this.ctx.fillText("# Entities:",x+78,y+60);
this.ctx.fillText("# Remaining:",x+78,y+72);
this.ctx.fillText("Entity ID:",x+78,y+84);
this.ctx.fillText("Activity:",x+78,y+96);
};
this.drawValues = function(start,current,end,numGenerated,numRemaining,ID,activity) {
this.ctx.font = "12px Arial";
this.ctx.fillStyle = "#FF0000";
this.ctx.textAlign = "left";
this.ctx.fillText(start.toFixed(5),this.xLocation+83,this.yLocation+24);
this.ctx.fillText(current.toFixed(5),this.xLocation+83,this.yLocation+36);
this.ctx.fillText(end.toFixed(5),this.xLocation+83,this.yLocation+48);
this.ctx.fillText(numGenerated.toFixed(0),this.xLocation+83,this.yLocation+60);
this.ctx.fillText(numRemaining.toFixed(0),this.xLocation+83,this.yLocation+72);
this.ctx.fillText(ID,this.xLocation+83,this.yLocation+84);
this.ctx.fillText(activity,this.xLocation+83,this.yLocation+96);
};
this.clearCanvas("#000000");
this.initBall = function(x,y) {
this.xDemo = x;
this.yDemo = y;
this.yVelocity = 0.0;
}
this.yVelocity = 0.0;
this.demo2 = function() {
this.clearCanvas("#000000");
this.drawEntry(20,20,150,100,"#FFFF00");
this.drawValues(
displayStartTime,
displayCurrentTime,
displayEndTime,
displayEntitiesScheduled,
(displayEntitiesScheduled-displayEntitiesGenerated),
displayEntityID,
displayActivity
);
//this.drawCircle(350.0,50.0,30.0,"#FF0000");
this.drawCircle(this.xDemo,this.yDemo,10.0,"#00FF00");
this.xDemo += 2.0;
this.yDemo += this.yVelocity;
this.yVelocity += 0.3;
if (this.yDemo > (this.height + 30)) {
//if (this.yDemo > (this.height - 50)) {
running = false;
cancelAnimationFrame(requestID);
}
};
this.endMessage = function() {
this.ctx.font = "12px Arial";
this.ctx.fillStyle = "#FF00FF";
this.ctx.textAlign = "left";
this.ctx.fillText("Simulation has ended",20.0,150.0);
}
};
var bcAnimation = new Animation(400,400,"bcanimation");
bcAnimation.drawEntry(20,20,150,120,"#FFFF00");
// bcAnimation.drawValues(180.0,203.765,210.0,4,1,27);
bcAnimation.initBall(180,70);
// bcAnimation.demo2();
//running = true;
function animateCircle() {
bcAnimation.demo2();
if (running) {
requestID = window.requestAnimationFrame(animateCircle);
}
}
//animateCircle();
//requestID = window.requestAnimationFrame(animateCircle);
var element = document.getElementById("step_button");
element.innerHTML = "Step";
function stepClick() {
//do an iteration of the main event loop from the DES section
if (keepRunning) {
ceq.processCeq();
var itemList = feq.getFirstItem();
if (itemList) {
feqCurrent = itemList[0];
feqCurrent.entity.activate();
bcAnimation.clearCanvas("#000000");
bcAnimation.drawEntry(20,20,150,100,"#FFFF00");
bcAnimation.drawValues(
displayStartTime,
displayCurrentTime,
displayEndTime,
displayEntitiesScheduled,
(displayEntitiesScheduled-displayEntitiesGenerated),
displayEntityID,
displayActivity
);
if (initBallFlag) {
running = true;
bcAnimation.initBall(180,70);
requestID = window.requestAnimationFrame(animateCircle);
initBallFlag = false;
}
} else {
keepRunning = false;
bcAnimation.endMessage();
}
}
}
</script>
</body>
</html>