diff --git a/images/bar.png b/images/bar.png
new file mode 100755
index 0000000..31c6b56
Binary files /dev/null and b/images/bar.png differ
diff --git a/images/drunken.gif b/images/drunken.gif
new file mode 100755
index 0000000..f555bb8
Binary files /dev/null and b/images/drunken.gif differ
diff --git a/images/drunken.jpg b/images/drunken.jpg
new file mode 100755
index 0000000..19f20ae
Binary files /dev/null and b/images/drunken.jpg differ
diff --git a/images/trans-white.png b/images/trans-white.png
new file mode 100755
index 0000000..89ead2d
Binary files /dev/null and b/images/trans-white.png differ
diff --git a/index.html b/index.html
new file mode 100755
index 0000000..1fc25df
--- /dev/null
+++ b/index.html
@@ -0,0 +1,26 @@
+
+
+ Drunken Man Simulator
+
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/libs/Debugger.js b/scripts/libs/Debugger.js
new file mode 100755
index 0000000..2992deb
--- /dev/null
+++ b/scripts/libs/Debugger.js
@@ -0,0 +1,18 @@
+
+/*
+ * class for debugging
+ */
+function Debug () {
+}
+
+// logs status messages with time in console.log
+Debug.log = function (source, text) {
+ var date = new Date();
+ var string = date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + "." + date.getMilliseconds();
+ for (var i = string.length; i < 15; i++)
+ string += " ";
+ string += " from " + source + ": " + text + "...";
+ console.log(string);
+}
+
+Debug.log("Debug", "module loaded");
diff --git a/scripts/libs/Graphics.js b/scripts/libs/Graphics.js
new file mode 100755
index 0000000..763fbbd
--- /dev/null
+++ b/scripts/libs/Graphics.js
@@ -0,0 +1,177 @@
+Debug.log("Graphics", "module loaded");
+
+/*
+ * class for graphic output
+ */
+function Graphics() {
+ Debug.log("Graphics", "instance created");
+ // some var init
+ this.zoom = 1;
+ this.displayPaths = false;
+ this.displayDistance = false;
+ this.displayBar = false;
+ this.displayDrunken = false;
+ this.drunken = new Drunken();
+ this.bar = new Bar();
+ this.null = new Position(window.innerWidth / 2, window.innerHeight / 2);
+}
+
+Graphics.prototype.canvas; // contains the canvas element
+Graphics.prototype.context; // contains the context for drawing
+Graphics.prototype.drunken; // contains the drunken instance
+Graphics.prototype.bar; // contains the bar instance
+Graphics.prototype.zoom; // contains the current zoom factor
+Graphics.prototype.null; // contains the null position
+Graphics.prototype.displayPaths; // true, if the path should be drawn
+Graphics.prototype.displayDistance; // true, if the distance between bar and drunken should be drawn
+Graphics.prototype.displayBar; // true, if the graphical bar should be drawn
+Graphics.prototype.displayDrunken; // true, if the graphical drunken should be drawn
+
+// init function
+Graphics.prototype.init = function () {
+ this.generateHTML();
+}
+
+// generates basic html
+Graphics.prototype.generateHTML = function () {
+ var container = document.createElement("div");
+ container.id = "container";
+ var canvas = document.createElement("canvas");
+ canvas.id = "canvas";
+ canvas.style.backgroundColor = "#becfdf";
+ canvas.style.position = "absolute";
+ canvas.style.top = "0px";
+ canvas.style.left = "0px";
+ canvas.style.cursor = "pointer";
+ this.canvas = canvas;
+ this.context = canvas.getContext('2d');
+ container.appendChild(canvas);
+ document.body.appendChild(container);
+ Debug.log("Graphics", "basic html inserted");
+ this.updateStyle();
+}
+
+// updates styles (called on resize)
+Graphics.prototype.updateStyle = function () {
+ this.canvas.height = window.innerHeight;
+ this.canvas.width = window.innerWidth;
+}
+
+// draws all
+Graphics.prototype.tick = function(path) {
+ // draws background
+ this.drawAlphaBackground();
+ // moves canvas null position to null position
+ this.context.translate(this.null.x, this.null.y);
+
+ // display drunken
+ if (this.displayDrunken)
+ this.drawDrunken(this.drunken);
+ else
+ this.drawDrunkenPoint(this.drunken);
+
+ // displays bar
+ if (this.displayBar)
+ this.drawBar(this.bar);
+ else
+ this.drawBarPoint(this.bar);
+
+ // displays path
+ if (this.displayPaths)
+ this.drawPath(path);
+
+ // displays distance
+ if (this.displayDistance)
+ this.drawDistance();
+
+ // moves canvas null position back to upper left corner
+ this.context.translate(-this.null.x, -this.null.y);
+}
+
+// draws background
+Graphics.prototype.drawAlphaBackground = function() {
+ this.context.beginPath();
+ this.context.fillStyle = "rgba(190, 207, 223, 1)";
+ this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
+ this.context.stroke();
+}
+
+// draws drunken graphical
+Graphics.prototype.drawDrunken = function(drunken) {
+ this.context.save();
+ this.context.translate(this.zoom * drunken.position.x, this.zoom * drunken.position.y);
+ var frameWidth = drunken.image.width / drunken.numberOfFrames;
+ var frameHeight = drunken.image.height;
+ var xOffset = frameWidth * drunken.step;
+ this.context.rotate(drunken.rotation);
+ this.context.drawImage(drunken.image, xOffset, 0, frameWidth, frameHeight, - frameWidth/ 2 * this.zoom, - frameHeight / 2 * this.zoom, this.zoom * frameWidth, this.zoom * frameHeight);
+ this.context.stroke();
+ this.context.restore();
+}
+
+// draws drunken
+Graphics.prototype.drawDrunkenPoint = function (drunken) {
+ this.context.beginPath();
+ this.context.save();
+ this.context.translate(this.zoom * drunken.position.x, this.zoom * drunken.position.y);
+ this.context.strokeStyle = "#f00";
+ this.context.arc(0, 0, drunken.step + 10, 0, Math.PI * 2, true);
+ this.context.stroke();
+ this.context.restore();
+}
+
+// draws bar graphical
+Graphics.prototype.drawBar = function(bar) {
+ this.context.save();
+ this.context.translate(this.zoom * bar.position.x, this.zoom * bar.position.y);
+ this.context.rotate(bar.rotation);
+ this.context.drawImage(bar.image, 0, 0, bar.image.width, bar.image.height, - bar.image.width / 2 * this.zoom, - bar.image.height / 2 * this.zoom, this.zoom * bar.image.width, this.zoom * bar.image.height)
+ this.context.stroke();
+ this.context.restore();
+}
+
+// draws bar
+Graphics.prototype.drawBarPoint = function (bar) {
+ this.context.beginPath();
+ this.context.save();
+ this.context.translate(this.zoom * bar.position.x, this.zoom * bar.position.y);
+ this.context.strokeStyle = "#00f";
+ this.context.arc(0, 0, 15 - this.drunken.step, 0, Math.PI * 2, true);
+ this.context.stroke();
+ this.context.restore();
+}
+
+// default/begin image
+Graphics.prototype.drawDefault = function () {
+ // pointless at the moment
+ // but maybe used in a later version
+}
+
+// draws path
+Graphics.prototype.drawPath = function(path) {
+ this.context.save();
+ this.context.beginPath();
+ this.context.lineCap = "round";
+ this.context.strokeStyle = "#fff";
+ var old = path.positions[0];
+ for (var i = 1; i < path.positions.length; i++) {
+ this.context.moveTo(old.x * this.zoom, old.y * this.zoom);
+ var now = path.positions[i];
+ this.context.lineTo(now.x * this.zoom, now.y * this.zoom);
+ old = now;
+ }
+ this.context.stroke();
+ this.context.restore();
+}
+
+// draws distance
+Graphics.prototype.drawDistance = function () {
+ this.context.save();
+ this.context.beginPath();
+ this.context.lineCap = "round";
+ this.context.strokeStyle = "#f00";
+ this.context.moveTo(0, 0);
+ this.context.lineTo(this.drunken.position.x * this.zoom, this.drunken.position.y * this.zoom);
+ this.context.stroke();
+ this.context.restore();
+}
diff --git a/scripts/libs/Miscellaneous.js b/scripts/libs/Miscellaneous.js
new file mode 100755
index 0000000..6270a55
--- /dev/null
+++ b/scripts/libs/Miscellaneous.js
@@ -0,0 +1,50 @@
+Debug.log("Miscellaneous", "module loaded");
+
+/*
+ * calculates page loading time
+ */
+function calcLoadTime() {
+ var startTime = new Date().getTime();
+ window.setTimeout(function() {
+ var endTime = new Date().getTime();
+ var time = endTime - startTime;
+ Miscellaneous.prototype.loadTime = time;
+ Debug.log("Miscellaneous", "page loading time calculated: " + time + "ms");
+ }, 0);
+}
+calcLoadTime();
+// removes function
+calcLoadTime = null;
+
+// add static mathode sign to Math class
+Math.sign = function(x) {
+ return (x > 0) ? 1 : (x < 0) ? -1 : 0;
+}
+
+/*
+ * class for all functions, which don't fit into other classes
+ */
+function Miscellaneous () {
+ Debug.log("Miscellaneous", "instance created");
+}
+
+// include function
+// includes file and excecuts load once the file is fully loaded, parsed and useable
+Miscellaneous.prototype.include = function (file, load) {
+ var script = document.createElement("script");
+ script.type = "text/javascript";
+ script.onload = load;
+ script.src = file;
+ document.head.appendChild(script);
+ Debug.log("Miscellaneous", "file included \"" + file + "\"");
+}
+
+// this function get's sopported style props
+// used for css3 manipulation in javascript
+Miscellaneous.prototype.getsupportedprop = function (proparray) {
+ var root = document.documentElement;
+ for (var i = 0; i < proparray.length; i++)
+ if (typeof root.style[proparray[i]] == "string")
+ return proparray[i]
+}
+
diff --git a/scripts/libs/Position.js b/scripts/libs/Position.js
new file mode 100755
index 0000000..8be024d
--- /dev/null
+++ b/scripts/libs/Position.js
@@ -0,0 +1,27 @@
+Debug.log("Position", "module loaded");
+
+/*
+ * position class
+ */
+function Position(x, y) {
+ // if arguments x, y are set
+ if (x)
+ this.x = x;
+ else
+ this.x = 0;
+ if (y)
+ this.y = y;
+ else
+ this.y = 0;
+}
+
+// yeah
+Position.prototype.x;
+Position.prototype.y;
+
+// returns true if argument and this are the same position
+Position.prototype.equals = function (position) {
+ if (this.x == position.x && this.y == position.y)
+ return true;
+ return false;
+}
diff --git a/scripts/main.js b/scripts/main.js
new file mode 100755
index 0000000..244c7f5
--- /dev/null
+++ b/scripts/main.js
@@ -0,0 +1,371 @@
+
+/*
+ * global vars
+ */
+var lengthOfStep = 10; // distance between edges; in graphical mode -> 40
+var numberOfEdges = 10; // will be set in start dialog
+var defTicksPerEdge = 33; // ticks for the drunken man to reach the next edge
+var numberOfTicksPerSec = 33; // sets tickrate (= framerate)
+
+var useTicksPerEdge = defTicksPerEdge; // for "even faster"
+
+var misc; // contains the Miscellaneous instance
+var graphics; // contains the Graphics instance
+var menu; // contains the Menu instance
+var path; // contains the path for the drunken man
+
+var edge; // contains the active edge
+var ticksPerEdge; // contains the number of ticks to reach the next edge
+var tickNum; // contains the global number of ticks
+var started; // true, if simulation is started
+var paused; // true, if simulation end is reached
+
+var checked; // contains the number of loaded includes
+var neededChecked; // contains the number of includes
+
+var dragEnabled; // true, if canvas element is clicked
+var dragInit; // true, if initial position is not defined yet
+var dragPosition; // contains the last mouse position
+var tickedDragPosition; // contains activ mouse position
+
+var tickId; // contains the interval id of the main ticker
+var fasterId; // contains the interval if of the "even faster" ticker
+
+var startpop; // contains the startup dialog
+var simTime; // contains the simulation time
+
+/*
+ * setup routine
+ */
+function setup () {
+ Debug.log("main", "setup routine started");
+
+ // some var init...
+ misc = new Miscellaneous();
+ checked = 0;
+ tickNum = 0;
+ started = false;
+ paused = false;
+ edge = 0;
+ ticksPerEdge = useTicksPerEdge;
+ dragEnabled = false;
+ dragInit = false;
+ fasterId = 0;
+ simTime = 0;
+
+ // includes for all modules
+
+ neededChecked = 7;
+
+ misc.include("scripts/libs/Graphics.js", check);
+ misc.include("scripts/libs/Position.js", check);
+
+ misc.include("scripts/objects/Path.js", check);
+ misc.include("scripts/objects/Menu.js", check);
+ misc.include("scripts/objects/Drunken.js", check);
+ misc.include("scripts/objects/Bar.js", check);
+ misc.include("scripts/objects/Popup.js", check);
+}
+
+/*
+ * check routine for includes
+ */
+function check() {
+ Debug.log("main", "load checked: " + ++checked + "/" + neededChecked);
+
+ // if all modules are loaded execute main routine
+ if (checked == neededChecked)
+ main();
+}
+
+/*
+ * main routine
+ */
+function main() {
+ Debug.log("main", "main routine started");
+
+ // some other var init
+ graphics = new Graphics();
+ menu = new Menu();
+ path = new Path();
+ dragPosition = new Position();
+ tickedDragPosition = new Position();
+
+ // module init
+ graphics.init();
+ menu.init();
+
+ // pointless, because there isn't a default image to draw ; )
+ graphics.drawDefault();
+
+ // set all (most) events
+ setEvents();
+
+ // generate startup dialog
+ startpop = new Popup("Drunken Man Simulator");
+ startpop.text = "";
+ startpop.text += 'Options:
';
+ startpop.text += 'use graphical mode
';
+ startpop.text += 'Please select a time:
';
+ startpop.text += '
';
+ startpop.text += '
';
+ startpop.display();
+}
+
+/*
+ * sets all (most) events
+ */
+function setEvents() {
+ window.onmousewheel = scroll;
+ window.onmousemove = drag;
+ document.getElementById("canvas").onmousedown = enableDrag;
+ window.onmouseup = disableDrag;
+ tickId = window.setInterval(tick, 1000 / numberOfTicksPerSec);
+ window.setInterval(infoUpdate, 100);
+ window.onresize = updateAllStyles;
+}
+
+/*
+ * tick function for "even faster"
+ */
+function fastTick() {
+
+ // if not started, than do nothing...
+ if (!started) return;
+
+ // if end not reached
+ if (edge != numberOfEdges) {
+
+ // calculate new Position and decrement current ticksPerEdge
+ graphics.drunken.position = graphics.drunken.getNextPosition(path.positions[edge], ticksPerEdge--);
+
+ // calculate rotation for graphical mode
+ graphics.drunken.rotation = graphics.drunken.getNextRotation(path.positions[edge]);
+
+ // if next edge is reached: increment edge number and reset ticksPerEdge
+ if (graphics.drunken.position.equals(path.positions[edge])) {
+ edge++;
+ ticksPerEdge = useTicksPerEdge;
+ }
+
+ // calculate steps for animation
+ graphics.drunken.step = graphics.drunken.frames[parseInt((tickNum / (5)) % graphics.drunken.frames.length)];
+ }
+
+ // if end reached stop simulation
+ if (edge == numberOfEdges)
+ stopSimulation();
+
+ // increment tick counter
+ tickNum++;
+}
+
+/*
+ * normal tick function
+ */
+function tick() {
+
+ // the same as fastTick
+ if (!started) return;
+
+ // if fast tick is disabled
+ if ((edge != numberOfEdges) && (!fasterId)) {
+ graphics.drunken.position = graphics.drunken.getNextPosition(path.positions[edge], ticksPerEdge--);
+ graphics.drunken.rotation = graphics.drunken.getNextRotation(path.positions[edge]);
+ if (graphics.drunken.position.equals(path.positions[edge])) {
+ edge++;
+ ticksPerEdge = useTicksPerEdge;
+ }
+ graphics.drunken.step = graphics.drunken.frames[parseInt((tickNum / (5)) % graphics.drunken.frames.length)];
+ }
+
+ // drag routine for canvas element
+ if (!dragPosition.equals(tickedDragPosition)) {
+ if (!(dragInit)) {
+ var deltaX = dragPosition.x - tickedDragPosition.x;
+ var deltaY = dragPosition.y - tickedDragPosition.y;
+
+ // sets null position in graphics
+ graphics.null.x += deltaX;
+ graphics.null.y += deltaY;
+ } else {
+ dragInit = false;
+ }
+ tickedDragPosition.x = dragPosition.x;
+ tickedDragPosition.y = dragPosition.y;
+ }
+
+ // update graphics
+ graphics.tick(path);
+
+ // if end is reached than stop simulation
+ if (!paused && (edge == numberOfEdges))
+ stopSimulation();
+ if (!fasterId)
+ tickNum++;
+}
+
+/*
+ * scroll function
+ */
+function scroll(e) {
+ e = e ? e : window.event;
+ var wheelData = e.detail ? e.detail : e.wheelDelta;
+ if (wheelData > 0)
+
+ // on positiv scroll zoom in
+ graphics.zoom *= 1.1;
+ else
+
+ // on negativ scroll zomm out
+ graphics.zoom /= 1.1;
+}
+
+/*
+ * updates debug box
+ */
+function infoUpdate() {
+ var text = "";
+ text += "drunken: x=" + graphics.drunken.position.x + " y=" + graphics.drunken.position.y + " rot=" + graphics.drunken.rotation + "
";
+ text += "bar: x=" + graphics.bar.position.x + " y=" + graphics.bar.position.y + " rot=" + graphics.bar.rotation + "
";
+ text += "edges: " + edge + "/" + numberOfEdges + "
";
+ document.getElementById("debugInfo").innerHTML = text;
+}
+
+/*
+ * enables drag for canvas element
+ */
+function enableDrag (e) {
+ dragEnabled = true;
+ dragInit = true;
+ document.getElementById("canvas").style.cursor = "move";
+}
+
+/*
+ * disables drag for canvas element
+ */
+function disableDrag () {
+ dragEnabled = false;
+ document.getElementById("canvas").style.cursor = "pointer";
+}
+
+/*
+ * drag function
+ */
+function drag (e) {
+
+ // check drag on all popups
+ Popup.checkDrag(e);
+
+ // check drag for canvas element
+ if (!dragEnabled)
+ return;
+ dragPosition.x = e.clientX;
+ dragPosition.y = e.clientY;
+}
+
+/*
+ * select function for start popup
+ */
+function selectOption (select, other) {
+ switch(other) {
+ case 1:
+ // numberOfEdges = select
+ Debug.log("main", (numberOfEdges = select) + " edges selected");
+ break;
+ case 0:
+ var frames;
+ // frames = select * numberOfTicksPerSec * 60; numberOfEdges = round(frames / defTicksPerEdge)
+ // select in min
+ Debug.log("main", select + " minutes selected -> " + (frames = select * numberOfTicksPerSec * 60) + " frames -> " + (numberOfEdges = Math.round(frames / defTicksPerEdge)) + " edges");
+ simTime = frames / numberOfTicksPerSec;
+ break;
+
+ // on other == undefined
+ default:
+ var frames;
+ // if value == 0 -> display other block
+ if (select.value == 0) {
+ document.getElementById("other").style.display = "block";
+ return;
+ }
+ // same as case 1, but select is object
+ // select.value in min
+ Debug.log("main", select.value + " minutes selected -> " + (frames = select.value * numberOfTicksPerSec * 60) + " frames -> " + (numberOfEdges = Math.round(frames / defTicksPerEdge)) + " edges");
+ simTime = frames / numberOfTicksPerSec;
+ }
+
+ // if graphical mode, then displayBar, displayDrunken, lengthOfStep = 40
+ if (document.getElementById("graphical").checked) {
+ lengthOfStep = 40;
+ graphics.displayBar = true;
+ graphics.displayDrunken = true;
+ document.getElementById("displayBar").checked = true;
+ document.getElementById("displayDrunken").checked = true;
+ }
+
+ // calculate new path
+ path = new Path();
+ path.calculate(0, 0, numberOfEdges - 1);
+
+ // close popup
+ startpop.close();
+
+ // start simulation
+ started = true;
+}
+
+/*
+ * for the "start simulation" button
+ */
+function selectOther () {
+ selectOption(parseInt(document.getElementById("val").value), parseInt(document.getElementById("siz").value));
+}
+
+/*
+ * on resize update all styles
+ */
+function updateAllStyles () {
+ graphics.updateStyle();
+ menu.updateStyle();
+}
+
+/*
+ * is called when simulation end is reached
+ */
+function stopSimulation() {
+ paused = true;
+
+ // reset all ticks
+ if (fasterId) {
+ window.clearInterval(fasterId);
+ fasterId = 0;
+ } else {
+ window.clearInterval(tickId);
+ tickId = window.setInterval(tick, 1000 / numberOfTicksPerSec);
+ }
+
+ // yeah, what do you think?
+ generateEndPopup();
+}
+
+/*
+ * generate the popup for the end
+ */
+function generateEndPopup () {
+ var endPop = new Popup("simulation report");
+ endPop.text = "";
+ // if simulation time is set -> display time & frames
+ if (simTime) {
+ endPop.text += "simulation time: " + simTime + "s
";
+ endPop.text += "frames: " + simTime * numberOfTicksPerSec + " (@ " + numberOfTicksPerSec + " fps)
";
+ }
+ endPop.text += "edges: " + numberOfEdges + "
";
+ endPop.text += "distance: " + Math.sqrt(graphics.drunken.position.x * graphics.drunken.position.x + graphics.drunken.position.y * graphics.drunken.position.y) + "
";
+ // calculated distance is the sqrt of time (einstein)
+ endPop.text += "calculated distance: " + Math.sqrt(path.positions.length);
+ endPop.display();
+}
diff --git a/scripts/objects/Bar.js b/scripts/objects/Bar.js
new file mode 100755
index 0000000..248c660
--- /dev/null
+++ b/scripts/objects/Bar.js
@@ -0,0 +1,19 @@
+Debug.log("Bar", "module loaded");
+
+/*
+ * class for the bar
+ */
+function Bar () {
+ Debug.log("Bar", "instance created");
+ this.position = new Position();
+ this.rotation = 0;
+ this.image = new Image();
+ this.image.src = "images/bar.png";
+}
+
+// image for graphics
+Bar.prototype.image;
+// rotaion for graphics
+Bar.prototype.rotation;
+// position
+Bar.prototype.position;
diff --git a/scripts/objects/Drunken.js b/scripts/objects/Drunken.js
new file mode 100755
index 0000000..2ac8d29
--- /dev/null
+++ b/scripts/objects/Drunken.js
@@ -0,0 +1,69 @@
+Debug.log("Drunken", "module loaded");
+
+/*
+ * class for the drunken man
+ */
+function Drunken () {
+ Debug.log("Drunken", "instance created");
+ // some var init
+ this.step = 1;
+ this.position = new Position();
+ this.rotation = 0;
+ this.image = new Image();
+ this.image.src = "images/drunken.gif";
+ this.numberOfFrames = 8;
+
+ // definition of the frames (order)
+ this.frames = Array();
+ this.frames[0] = 4;
+ this.frames[1] = 3;
+ this.frames[2] = 2;
+ this.frames[3] = 1;
+ this.frames[4] = 0;
+ this.frames[5] = 1;
+ this.frames[6] = 2;
+ this.frames[7] = 3;
+ this.frames[8] = 4;
+ this.frames[9] = 5;
+}
+
+// rotation for graphics
+Drunken.prototype.rotation;
+// position
+Drunken.prototype.position;
+// current animation step
+Drunken.prototype.step;
+// how many frames does the animation have?
+Drunken.prototype.numberOfFrames;
+// array for the frame order
+Drunken.prototype.frames;
+
+// calculates new position and returns it
+Drunken.prototype.getNextPosition = function(nextEdge, ticksToNext) {
+ /* safty first */
+ if (ticksToNext < 0) {
+ this.position = nextEdge;
+ return this.position;
+ }
+
+ // split distance to x,y
+ var deltaX = nextEdge.x - this.position.x;
+ var deltaY = nextEdge.y - this.position.y;
+ // calulate step width for next tick
+ var xPerTick = deltaX / ticksToNext;
+ var yPerTick = deltaY / ticksToNext;
+ // create new position instance with new coordinates
+ return new Position(xPerTick + this.position.x, yPerTick + this.position.y);
+}
+
+// calculates new rotation and returns it
+Drunken.prototype.getNextRotation = function(nextEdge) {
+ var deltaX = nextEdge.x - this.position.x;
+ var deltaY = nextEdge.y - this.position.y;
+ var direct = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+ if (direct == 0)
+ var rot = 0;
+ else
+ var rot = Math.acos(deltaX / direct) * Math.sign(deltaY) - Math.PI / 2;
+ return rot;
+}
diff --git a/scripts/objects/Menu.js b/scripts/objects/Menu.js
new file mode 100755
index 0000000..2c6fe9e
--- /dev/null
+++ b/scripts/objects/Menu.js
@@ -0,0 +1,231 @@
+Debug.log("Menu", "module loaded");
+
+/*
+ * class for menu and basic html
+ */
+function Menu() {
+ Debug.log("Menu", "instance created");
+}
+
+// contains the dropdown dom object
+Menu.prototype.dropDown;
+// is the dropdown menu collapsed
+Menu.prototype.folded = true;
+
+// init
+Menu.prototype.init = function () {
+ this.generateHTML();
+}
+
+// generates basic HTML and inserts it
+Menu.prototype.generateHTML = function () {
+ var container = document.createElement("div");
+ container.id = "menuContainer";
+ container.style.backgroundColor = "#19446b";
+ container.style.position = "absolute";
+ container.style.height = "25px";
+ container.style.top = "0px";
+ container.style.left = "0px";
+ document.body.appendChild(container);
+
+ var dropDown = document.createElement("div");
+ dropDown.id = "menuDropDown";
+ dropDown.style.width = "200px";
+ dropDown.style.height = "25px";
+ dropDown.style.position = "absolute";
+ dropDown.style.top = "0px";
+ dropDown.style.overflow = "hidden";
+ dropDown.style.left = "0px";
+ dropDown.style.backgroundColor = "#6395c4";
+ container.appendChild(dropDown);
+ this.dropDown = dropDown;
+
+ var dropDownButton = document.createElement("div");
+ dropDownButton.innerHTML = "Debug Settings";
+ dropDownButton.onclick = menu.fold;
+ dropDownButton.style.backgroundColor = "#4d7ba5";
+ dropDownButton.style.height = "25px";
+ dropDown.appendChild(dropDownButton);
+
+ var dropDownControls = document.createElement("div");
+ dropDownControls.id = "dropdownControls";
+ dropDownControls.style.position = "relative";
+ dropDownControls.style.left = "0px";
+ dropDown.appendChild(dropDownControls);
+
+ var tmpDiv = document.createElement("div");
+ var check = document.createElement("input");
+ check.id = "displayDebug";
+ check.type = "checkbox";
+ tmpDiv.appendChild(check);
+ tmpDiv.innerHTML += "display debug info";
+ dropDownControls.appendChild(tmpDiv);
+ document.getElementById("displayDebug").onclick = menu.debugCheck;
+
+ tmpDiv = document.createElement("div");
+ check = document.createElement("input");
+ check.id = "displayPath";
+ check.type = "checkbox";
+ tmpDiv.appendChild(check);
+ tmpDiv.innerHTML += "display path";
+ dropDownControls.appendChild(tmpDiv);
+ document.getElementById("displayPath").onclick = menu.pathCheck;
+
+ tmpDiv = document.createElement("div");
+ check = document.createElement("input");
+ check.id = "displayDistance";
+ check.type = "checkbox";
+ tmpDiv.appendChild(check);
+ tmpDiv.innerHTML += "display distance to bar";
+ dropDownControls.appendChild(tmpDiv);
+ document.getElementById("displayDistance").onclick = menu.distanceCheck;
+
+ tmpDiv = document.createElement("div");
+ check = document.createElement("input");
+ check.id = "displayBar";
+ check.type = "checkbox";
+ tmpDiv.appendChild(check);
+ tmpDiv.innerHTML += "display bar";
+ dropDownControls.appendChild(tmpDiv);
+ document.getElementById("displayBar").checked = false;
+ document.getElementById("displayBar").onclick = menu.barCheck;
+
+ tmpDiv = document.createElement("div");
+ check = document.createElement("input");
+ check.id = "displayDrunken";
+ check.type = "checkbox";
+ tmpDiv.appendChild(check);
+ tmpDiv.innerHTML += "display drunken";
+ dropDownControls.appendChild(tmpDiv);
+ document.getElementById("displayDrunken").checked = false;
+ document.getElementById("displayDrunken").onclick = menu.drunkenCheck;
+
+ tmpDiv = document.createElement("div");
+ check = document.createElement("input");
+ check.id = "fastForward";
+ check.type = "checkbox";
+ tmpDiv.appendChild(check);
+ tmpDiv.innerHTML += "fast forward simulation";
+ dropDownControls.appendChild(tmpDiv);
+ document.getElementById("fastForward").setAttribute("onclick", "menu.fastForward();");
+
+ tmpDiv = document.createElement("div");
+ tmpDiv.id = "fasterForwardDiv";
+ check = document.createElement("input");
+ check.id = "fasterForward";
+ check.type = "checkbox";
+ check.style.marginLeft = "20px";
+ tmpDiv.appendChild(check);
+ tmpDiv.style.display = "none";
+ tmpDiv.innerHTML += "even faster";
+ dropDownControls.appendChild(tmpDiv);
+ document.getElementById("fasterForward").setAttribute("onclick", "menu.fastForward(1);");
+
+ var reset = document.createElement("button");
+ reset.innerHTML = "reset zoom and position";
+ reset.style.position = "absolute";
+ reset.style.left = "200px";
+ reset.onclick = menu.reset;
+ container.appendChild(reset);
+
+ var debug = document.createElement("div");
+ debug.id = "debugInfo";
+ debug.style.backgroundColor = "#618bb2";
+ debug.style.position = "absolute";
+ debug.style.bottom = "0px";
+ debug.style.right = "0px";
+ debug.style.height = "100px";
+ debug.style.width = "300px";
+ debug.style.display = "none";
+ document.body.appendChild(debug);
+
+ Debug.log("Menu", "basic html inserted");
+ this.updateStyle();
+}
+
+// refreshes values (for onresize)
+Menu.prototype.updateStyle = function () {
+ document.getElementById("menuContainer").style.width = window.innerWidth + "px";
+}
+
+// function for the "debug info" checkbox
+Menu.prototype.debugCheck = function () {
+ document.getElementById("debugInfo").style.display = document.getElementById("displayDebug").checked ? "block" : "none";
+}
+
+// function for the "display path" checkbox
+Menu.prototype.pathCheck = function () {
+ graphics.displayPaths = document.getElementById("displayPath").checked;
+}
+
+// function for the "display distance" checkbox
+Menu.prototype.distanceCheck = function () {
+ graphics.displayDistance = document.getElementById("displayDistance").checked;
+}
+
+// function for the "display bar" checkbox
+Menu.prototype.barCheck = function () {
+ graphics.displayBar = document.getElementById("displayBar").checked;
+}
+
+// function for the "display drunken" checkbox
+Menu.prototype.drunkenCheck = function () {
+ graphics.displayDrunken = document.getElementById("displayDrunken").checked;
+}
+
+// function for "fast forward simulation" and "even faster"
+Menu.prototype.fastForward = function (faster) {
+ if (faster) {
+ if (fasterId) {
+ // clear faster tick interval
+ // and set normal tick to fast mode
+ useTicksPerEdge = defTicksPerEdge;
+ window.clearInterval(fasterId);
+ fasterId = 0;
+ menu.fastForward();
+ document.getElementById("fastForward").disabled = false;
+ } else {
+ // set normal tick to normal mode
+ // and set faster tick interval
+ useTicksPerEdge = defTicksPerEdge / 5;
+ fasterId = window.setInterval(fastTick, 1);
+ window.clearInterval(tickId);
+ tickId = window.setInterval(tick, 1000 / numberOfTicksPerSec);
+ document.getElementById("fastForward").disabled = true;
+ }
+ return;
+ }
+
+ // if fast forward is checked switch normal tick to fast mode
+ // and display "even faster" checkbox
+ if (document.getElementById("fastForward").checked) {
+ window.clearInterval(tickId);
+ tickId = window.setInterval(tick, 10);
+ document.getElementById("fasterForwardDiv").style.display = "block";
+ // else switch normal tick to normal mode
+ // and hide "ven faster" checkbox
+ } else {
+ window.clearInterval(tickId);
+ tickId = window.setInterval(tick, 1000 / numberOfTicksPerSec);
+ document.getElementById("fasterForwardDiv").style.display = "none";
+ }
+}
+
+// function for the reset button
+// resets zoom and centers null position
+Menu.prototype.reset = function () {
+ graphics.zoom = 1;
+ graphics.null = new Position(window.innerWidth / 2, window.innerHeight / 2);
+}
+
+// function for the dropdown menu
+// collaps the dropdown menu
+Menu.prototype.fold = function () {
+ if (menu.folded)
+ menu.dropDown.style.height = "auto";
+ else
+ menu.dropDown.style.height = "25px";
+ menu.folded = !menu.folded;
+
+ Debug.log("Menu", "dropdown menu is " + (menu.folded ? "closed" : "opened"));
+}
diff --git a/scripts/objects/Path.js b/scripts/objects/Path.js
new file mode 100755
index 0000000..0709400
--- /dev/null
+++ b/scripts/objects/Path.js
@@ -0,0 +1,34 @@
+Debug.log("Path", "module loaded");
+
+/*
+ * class for paths
+ */
+function Path() {
+ this.positions = new Array();
+ Debug.log("Path", "instance created");
+}
+
+// contains edge array
+Path.prototype.positions;
+
+// calculates new edges and adds them to positions
+// params: x, y -> start point
+// length -> number of random points to add
+Path.prototype.calculate = function (x, y, length) {
+ var position = new Position(x, y);
+ this.positions.push(position);
+ for (var i = 0; i < length; i++) {
+ position = this.getNext(position);
+ this.positions.push(position);
+ }
+ Debug.log("Path", "new path calculated");
+}
+
+// calculates next point
+Path.prototype.getNext = function (oldPosition) {
+ var rotation = Math.random() * 2 * Math.PI;
+ var result = new Position();
+ result.x = parseInt(Math.cos(rotation) * lengthOfStep + oldPosition.x);
+ result.y = parseInt(Math.sin(rotation) * lengthOfStep + oldPosition.y);
+ return result;
+}
diff --git a/scripts/objects/Popup.js b/scripts/objects/Popup.js
new file mode 100755
index 0000000..c5997e3
--- /dev/null
+++ b/scripts/objects/Popup.js
@@ -0,0 +1,154 @@
+Debug.log("Popup", "module loaded");
+
+/*
+ * class for dialogs
+ */
+// contructor argument is title of popup
+function Popup(title) {
+ Debug.log("Popup", "'" + title + "': instance created");
+
+ // some var init
+ this.title = title;
+ this.height = 400;
+ this.width = 400;
+ this.drag = false;
+ this.id = Popup.getId();
+ this.type = Popup.types.normal;
+ Popup.add(this);
+}
+
+// static field with references to all popups
+Popup.array = new Array();
+
+// static array (enum) for popup types (not implemented)
+Popup.types = {
+ normal: 1,
+ normalOkay: 2
+}
+
+// add popup to array
+Popup.add = function (popup) {
+ Popup.array.push(popup);
+}
+
+// get next id
+Popup.getId = function () {
+ return Popup.array.length;
+}
+
+// static function for handling drag
+// called on mousemove
+// argument is event object
+Popup.checkDrag = function (e) {
+ for(var i in Popup.array) {
+ if (Popup.array[i].drag)
+ Popup.array[i].move(e);
+ }
+}
+
+// enables drag for popup id (argument)
+// called on mousedown on title bar
+Popup.enableDrag = function (id) {
+ Debug.log("Popup", "id " + id + " drag enabled");
+ Popup.array[id].drag = true;
+ Popup.array[id].style.cursor = "move";
+}
+
+// diables drag for popup id (argument)
+// called on mouseup on title bar
+Popup.disableDrag = function (id) {
+ Debug.log("Popup", "id " + id + " drag disabled");
+ Popup.array[id].drag = false;
+ Popup.array[id].dragedPosition = false;
+ Popup.array[id].style.cursor = "pointer";
+}
+
+// closes popup id (argument)
+Popup.close = function (id) {
+ Popup.array[id].close();
+}
+
+// own id
+Popup.prototype.id;
+// popup title
+Popup.prototype.title;
+// popup inner text
+Popup.prototype.text;
+// popup type (not implemented)
+Popup.prototype.type;
+// popup height
+Popup.prototype.height;
+// popup width
+Popup.prototype.width;
+// contains reference to dom style
+Popup.prototype.style;
+// true, if drag is enabled
+Popup.prototype.drag;
+// contains last mouse position
+Popup.prototype.dragedPosition;
+
+// displays popup and generates html
+Popup.prototype.display = function() {
+ this.generateHTML();
+ this.style.display = "block";
+ Debug.log("Popup", "'" + this.title + "': opened");
+}
+
+// generates html
+Popup.prototype.generateHTML = function() {
+ Debug.log("Popup", "'" + this.title + "': basic html inserted");
+ var div = document.createElement("div");
+ this.style = div.style;
+ div.id = this.id;
+ div.style.height = this.height + "px";
+ div.style.width = this.width + "px";
+ div.style.backgroundColor = "#dfe7ee";
+ div.style.display = "none";
+ div.style.position = "absolute";
+ div.style.top = (window.innerHeight / 2 - this.height / 2) + "px";
+ div.style.left = (window.innerWidth / 2 - this.width / 2) + "px";
+ div.style.cursor = "pointer";
+ var taskbar = document.createElement("div");
+ taskbar.className = "bar";
+ taskbar.style.height = "20px";
+ taskbar.style.width = this.width + "px";
+ taskbar.style.backgroundColor = "#20588b";
+ taskbar.style.position = "absolute";
+ taskbar.style.top = 0;
+ taskbar.style.left = 0;
+ taskbar.innerHTML = '[x] ' + this.title;
+ div.appendChild(taskbar);
+ var text = document.createElement("div");
+ text.style.position = "absolute";
+ text.style.top = "20px";
+ text.innerHTML = this.text;
+ div.appendChild(text);
+ document.body.appendChild(div);
+
+ var bar = document.getElementById(this.id + "").getElementsByClassName("bar")[0];
+
+ // small hack for static function params
+ bar.onmousedown = eval("(function () { Popup.enableDrag(" + this.id + ")})");
+ bar.onmouseup = eval("(function () { Popup.disableDrag(" + this.id + ")})");
+}
+
+// hiddes popup
+Popup.prototype.close = function () {
+ Debug.log("Popup", "'" + this.title + "': closed")
+ this.style.display = "none";
+}
+
+// moves popup
+// argument is event object
+Popup.prototype.move = function (e) {
+ var position = new Position(e.clientX, e.clientY);
+ if (!this.dragedPosition) {
+ this.dragedPosition = position;
+ return;
+ }
+ var deltaX = position.x - this.dragedPosition.x;
+ var deltaY = position.y - this.dragedPosition.y;
+ this.style.top = (parseInt(this.style.top) + deltaY) + "px";
+ this.style.left = (parseInt(this.style.left) + deltaX) + "px";
+ this.dragedPosition = position;
+}