I'm trying to filter out all lines as a result from HoughLine function in opencv. Currently just using getLines() function to get all lines then sorting them according to their rho value for each line. Then applying for loop to remove duplicates, problem is as follows:
In code if in (r1, θ1) and (r2, θ2) which I'm comparing in inner for loop for both either (r1,r2) positive or (r1,r2) negative (based on html canvas) then it's easy to filter them. But if either of them is positve or negative respectively then that line also gets included in finalLines array. Which can be explained through following figure:
function:
function getLines(minthresh = 8) {
let t = 300,
j = 0;
linesMat = new cv.Mat();
let copy = org_img.clone();
while (t > 0 && j < minthresh) {
try {
cv.HoughLines(tmp, linesMat, 1, Math.PI / 180, t);
j = linesMat.matSize[0]
console.log(`t=${t} j=${j}`, linesMat.matSize);
} catch (err) {
console.log(`error for t=${t}`);
j = 0;
} finally {
t = t - 10;
}
}
console.log('hough lines', linesMat.rows);
let bool = true;
let lines = linesMat.data32F;
let rows = linesMat.rows;
let foundLines = [];
finalLines = [];
for (let i = 0; i < rows; i++) {
let l = [lines[i * 2], lines[i * 2 + 1]];
foundLines.push(l);
}
foundLines.sort((a, b) => a[0] - b[0]);
console.log('filtering lines starts, sorted found lines:', foundLines);
for (let [index, line] of foundLines.entries()) {
let rho = line[0];
let theta = line[1];
let a = Math.cos(theta);
let b = Math.sin(theta);
let x0 = a * rho;
let y0 = b * rho;
let startPoint = {
x: x0 - 1000 * b,
y: y0 + 1000 * a
};
let endPoint = {
x: x0 + 1000 * b,
y: y0 - 1000 * a
};
let color = [random(), random(), random(), 255];
cv.line(copy, startPoint, endPoint, color);
bool = false;
for (let [jindex, linet] of foundLines.slice(index + 1).entries()) {
console.log(`for index=${index} jindex=${jindex+1} linet=${linet} line=${line}`);
bool = false;
if (linet[0] !== line[0]) {
k = [Math.abs(linet[0] - line[0]) < 50, Math.abs(linet[1] - line[1]) < 0.5];
console.log(`k=${k}`);
if (k[0] && k[1]) {
bool = true;
break;
}
}
}
if (bool) {
continue;
}
finalLines.push(line);
console.log(`push for i=${index}`);
console.log(`%c rho=${rho} deg=${theta * (180/Math.PI)}`, `background: rgba(${color[0]}, ${color[1]}, ${color[2]}, 1); color: black`);
}
cv.imshow('op', copy);
}
Related
I've written a merge sort visualisation in p5.js which shows the steps of merge sort. This works fine as a sequential visualisation, but I'd quite like to show this as a true representation, where you can see each part of the array being sorted at the same time (with multiple sections being visualised sorting at the same time, to truly reflect the recursion). The code itself is relatively simple:
// Split the array recursively
let mid = Math.floor((right + left) / 2);
if (right - left < 1) {
return;
}
// My attempt to visualise this properly
await Promise.all([mergeSortSlice(array, left, mid), mergeSortSlice(array, mid + 1, right)]);
// THIS WORKS, but only for sequential sorting
// await mergeSortSlice(array, left, mid);
// await mergeSortSlice(array, mid + 1, right)
// Putting sleep(200) here also works, but doesn't show the steps of the sort as they are happening, just the result of each stage of the sort.
leftCounter = 0;
rightCounter = 0;
l = left;
r = mid + 1;
valuesStartIndex = l;
let leftArray = array.slice(left, r);
let rightArray = array.slice(r, right + 1);
while (rightCounter < rightArray.length && leftCounter < leftArray.length) {
if (leftArray[leftCounter] < rightArray[rightCounter]) {
array.splice(l + rightCounter, 1);
array.splice(valuesStartIndex, 0, leftArray[leftCounter]);
l++;
leftCounter++;
valuesStartIndex++;
await sleep(200);
} else {
array.splice(r, 1);
array.splice(valuesStartIndex, 0, rightArray[rightCounter]);
r++;
rightCounter++;
valuesStartIndex++;
await sleep(200);
}
}
The problem with using Promise.all is that the split parts of the array are getting mixed up, I believe due to the recursion? This is resulting in the array not getting sorted properly.
My timeout function:
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
The setup function and draw loop:
let values = [50, 10, 80, 56, 30, 25, 15]
function setup() {
createCanvas(600, 190);
frameRate(60);
mergeSort(values)
}
function draw() {
rectWidth = 10;
background(23);
stroke(0);
fill(255);
for (let i = 0; i < values.length; i++) {
rect(i * rectWidth, height - values[i], rectWidth, values[i]);
}
}
The combination of async functions and recursion makes it difficult for me to come up with a solution for this. Any help/advice would be much appreciated.
You were actually very close to having a working solution. Your issue is that you are creating a bunch of global variables inside your mergeSortSlice function:
// These were all missing the let keyword
// And were therefore either assigning or implicitly declaring
// globally scoped variables.
let leftCounter = 0;
let rightCounter = 0;
let l = left;
let r = mid + 1;
let valuesStartIndex = l;
let leftArray = array.slice(left, r);
let rightArray = array.slice(r, right + 1);
When two instances of a function invocation being run as part of a Promise each of which await on timeouts, their execution is going to be interleaved (which you want so you can graphically represent the theoretical parallelism). However, when those functions alter global variables this is a classic shared memory multi-threading bug.
Here's an adaptation of your code with the bug fixed, highlighting added, and a slightly different delay strategy:
function merge_sort(p) {
const Mode = {
Shuffling: 0,
Sorting: 1
};
const spacing = 5;
let array = [...Array(40)].map((_, i) => i);
let highlights = [];
let itemWidth;
let itemHeight;
let currentMode = Mode.Shuffling;
let iterator;
let frameRate = 8;
let redrawPromise;
let signalRedraw;
p.setup = function() {
p.createCanvas(p.windowWidth, p.windowHeight);
p.frameRate(frameRate * 5);
itemWidth = (p.width - (spacing * (array.length + 1))) / array.length;
itemHeight = p.height - spacing * 2;
iterator = shuffle();
initRedrawPromise();
};
function initRedrawPromise() {
redrawPromise =
new Promise(resolve => {
signalRedraw = resolve;
});
redrawPromise.then(() => initRedrawPromise());
}
p.draw = function() {
p.background('white');
// draw
for (let i = 0; i < array.length; i++) {
if (highlights[i]) {
p.fill(highlights[i]);
} else {
p.fill('blue');
}
let fractionalHeight = (array[i] + 1) / array.length;
let pixelHeight = fractionalHeight * itemHeight;
p.rect(
(i + 1) * spacing + i * itemWidth,
spacing + (itemHeight - pixelHeight),
itemWidth,
pixelHeight
);
}
signalRedraw();
if (currentMode === Mode.Shuffling) {
// update
let next = iterator.next();
if (next.value) {
// Done suffle, switch to sort
currentMode = Mode.Sorting;
p.frameRate(frameRate);
sort().then(() => {
// switch back to shuffling
currentMode = Mode.Shuffling;
p.frameRate(frameRate * 5);
iterator = shuffle();
});
}
}
};
p.keyPressed = function(e) {
if (e.key === 'ArrowRight' || e.key === 'ArrowUp') {
frameRate++;
p.frameRate(frameRate);
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') {
frameRate = Math.max(0, frameRate - 1);
p.frameRate(frameRate);
}
}
// shuffle the array. yield false for each step where the array is not yet shuffled. yield true once the array is shuffled.
function* shuffle() {
// for each position in the array (except the last position),
// if the chosen item is not the current item, swap the two items.
for (let i = 0; i < array.length - 1; i++) {
highlight(i);
yield false;
let j = randomInt(i, array.length);
if (j !== i) {
highlight(i, j);
yield false;
swap(i, j);
highlight(j, i);
yield false;
} else {
highlight(i);
yield false;
}
}
yield true;
}
function sort() {
highlights = [];
return sortSlice(0, array.length - 1);
}
async function sortSlice(left, right) {
if (right - left < 1) {
return;
}
// Split the array recursively
let mid = Math.floor((right + left) / 2);
await Promise.all([sortSlice(left, mid), sortSlice(mid + 1, right)]);
for (let ix = left; ix <= right; ix++) {
highlights[ix] = undefined;
}
let leftCounter = 0;
let rightCounter = 0;
let l = left;
let r = mid + 1;
let valuesStartIndex = l;
let leftArray = array.slice(left, r);
let rightArray = array.slice(r, right + 1);
while (rightCounter < rightArray.length && leftCounter < leftArray.length) {
if (leftArray[leftCounter] < rightArray[rightCounter]) {
array.splice(l + rightCounter, 1);
array.splice(valuesStartIndex, 0, leftArray[leftCounter]);
highlights[valuesStartIndex] = 'green';
highlights[r] = 'red';
l++;
leftCounter++;
valuesStartIndex++;
} else {
array.splice(r, 1);
array.splice(valuesStartIndex, 0, rightArray[rightCounter]);
highlights[valuesStartIndex] = 'green';
r++;
rightCounter++;
valuesStartIndex++;
highlights[l + rightCounter] = 'red';
}
// at each merge step wait for a redraw that shows this step
await redrawPromise;
highlights[valuesStartIndex - 1] = 'gray';
for (let ix = valuesStartIndex; ix <= right; ix++) {
highlights[ix] = undefined;
}
}
}
function swap(i, j) {
const tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
function randomInt(lowerBound, upperBound) {
return lowerBound + Math.floor(Math.random() * (upperBound - lowerBound));
}
function highlight(i, j) {
highlights = [];
if (i !== undefined) {
highlights[i] = 'green';
}
if (j !== undefined) {
highlights[j] = 'red';
}
}
}
sketch = new p5(merge_sort);
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
I'm new to JavaScript, I'm trying to solve leetcode question 37. I need to a create a blank two dimensional array, I initially used the method in the comments; however, it doesn't work correctly, it will change all the value. Then, I used the for loop method to create array and currently it worked correctly. But I still cannot figured out why this will happen, could anyone explain the reason why this will happen, is this because of shallow copy?
var solveSudoku = function (board) {
// let rows = new Array(9).fill(new Array(10).fill(0)),
let rows = new Array(9);
for (let i = 0; i < 9; i++) {
rows[i] = new Array(10).fill(0);
}
let cols = new Array(9);
for (let i = 0; i < 9; i++) {
cols[i] = new Array(10).fill(0);
}
let boxes = new Array(9);
for (let i = 0; i < 9; i++) {
boxes[i] = new Array(10).fill(0);
}
// let cols = new Array(9).fill(new Array(10).fill(0)),
// boxes = new Array(9).fill(new Array(10).fill(0));
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
let c = board[i][j];
if (c !== '.') {
let n = parseInt(c),
bx = Math.floor(j / 3),
by = Math.floor(i / 3);
// 0代表为使用,1为使用过
rows[i][n] = 1;
console.log(i, n)
cols[j][n] = 1;
// box索引
boxes[by * 3 + bx][n] = 1;
}
}
}
fill(board, 0, 0)
function fill(board, x, y) {
// 完成填充条件
if (y === 9) return true;
// 下一个点的坐标
let nx = (x + 1) % 9,
// 判断进入是否下一行
ny = (nx === 0) ? y + 1 : y;
// 如果已经填充,则进入下一个点
if (board[y][x] !== '.') return fill(board, nx, ny);
// 没有被填充过
for (let i = 1; i <= 9; i++) {
let bx = Math.floor(x / 3),
by = Math.floor(y / 3),
box_key = by * 3 + bx;
if (!rows[y][i] && !cols[x][i] && !boxes[box_key][i]) {
rows[y][i] = 1;
cols[x][i] = 1;
boxes[box_key][i] = 1;
board[y][x] = i.toString();
console.log(board[y][x])
// 递归向下一个点求解
if (fill(board, nx, ny)) return true;
// 恢复初始状态
board[y][x] = '.';
boxes[box_key][i] = 0;
rows[y][i] = 0;
cols[x][i] = 0;
}
}
return false;
}
console.log(board);
};
The problem with fill(), at least with object, is that it passes the same object, by reference, to all element of the array. So if you mutate this object, then it will mutate every object of every arrays.
Note that in your case, you are creating a new Array object using it's constructor ( new Array() ) which makes them objects.
const matrix = new Array(5).fill(new Array(5).fill(0));
console.log(matrix);
In the previous snippet, you can see that the values of the other rows, from the second one to the end, are reference to the initial row.
To get around that, you can fill you array with empty values and then use the map() to create unique object for each position in the array.
const matrix = new Array(5).fill().map(function() { return new Array(5).fill(0); });
console.log(matrix);
As you can see in the previous snippet, all the rows are now their unique reference.
This is the reason all of your values were changed.
I've applied this solution to your code. I wasn't able to test it, because I wasn't sure of the initial parameters to pass.
I've also used anonymous function here ( function() { return; } ), but I would success using arrow function ( () => {} ) instead, if you are comfortable with them. It's cleaner.
var solveSudoku = function (board) {
let rows = new Array(9).fill().map(function() { return new Array(10).fill(0); }),
cols = new Array(9).fill().map(function() { return new Array(10).fill(0); }),
boxes = new Array(9).fill().map(function() { return new Array(10).fill(0); });
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
let c = board[i][j];
if (c !== '.') {
let n = parseInt(c),
bx = Math.floor(j / 3),
by = Math.floor(i / 3);
// 0代表为使用,1为使用过
rows[i][n] = 1;
console.log(i, n)
cols[j][n] = 1;
// box索引
boxes[by * 3 + bx][n] = 1;
}
}
}
fill(board, 0, 0)
function fill(board, x, y) {
// 完成填充条件
if (y === 9) return true;
// 下一个点的坐标
let nx = (x + 1) % 9,
// 判断进入是否下一行
ny = (nx === 0) ? y + 1 : y;
// 如果已经填充,则进入下一个点
if (board[y][x] !== '.') return fill(board, nx, ny);
// 没有被填充过
for (let i = 1; i <= 9; i++) {
let bx = Math.floor(x / 3),
by = Math.floor(y / 3),
box_key = by * 3 + bx;
if (!rows[y][i] && !cols[x][i] && !boxes[box_key][i]) {
rows[y][i] = 1;
cols[x][i] = 1;
boxes[box_key][i] = 1;
board[y][x] = i.toString();
console.log(board[y][x])
// 递归向下一个点求解
if (fill(board, nx, ny)) return true;
// 恢复初始状态
board[y][x] = '.';
boxes[box_key][i] = 0;
rows[y][i] = 0;
cols[x][i] = 0;
}
}
return false;
}
console.log(board);
};
I get convergence, nevertheless, the results are never the same when the algorithm is refreshed. This occurs even when the data observations is the same dataset. Can anyone tell me where my methodology is wrong? For the life of me I can't figure out where the process is wrong.
function kmeans2(k, data, canvas, converge) {
this.canvas = jsHS.GetDimensions(canvas);
this.k = k;
this.centroids = []; // Array of centroids
this.centroids2compare = [];
this.data = data;
this.converge = converge;
this.init();
}
kmeans2.prototype.distance = function () {
var dif = 0,
iArray = jsHS.isArray(arguments);
if (iArray) {
if (arguments.length > 2) {
for (var i = 0; i < arguments.length; i+2) {
var p0 = arguments[i],
p1 = arguments[i + 1];
dif += Math.pow(p0[0] - p1[0], 2);
dif += Math.pow(p0[1] - p1[1], 2);
}
}
else {
var pd0 = arguments[0],
pd1 = arguments[1];
dif += Math.pow(pd0[0] - pd1[0], 2);
dif += Math.pow(pd0[1] - pd1[1], 2);
}
}
return Math.sqrt(dif);
};
kmeans2.prototype.Means = function (Array) {
var bin = 0;
[].forEach.call(Array, function(a){
bin += a;
});
return bin / Array.length;
};
kmeans2.prototype.init = function () {
for (var l = 0; l < this.k; l++) {
var dataItem = this.data[Math.floor(Math.random() * this.data.length)];
this.centroids.push(dataItem);
}
for (var i = 0; i < this.centroids.length; i++) {
if (i > 0) {
var distance = this.distance(this.centroids[i], this.centroids[i - 1]);
console.log(distance);
}
}
this.clusterCentroids(); // return centroid center after calculating means.
};
kmeans2.prototype.clusterCentroids = function () {
var points0 = [];
this.centroids2compare = this.centroids;
// Find distances between centroid and observations.
for (var d = 0; d < this.data.length; d++) {
var cinbin = [];
for (var c0 = 0; c0 < this.k; c0++) {
var dis = this.distance(this.centroids[c0], this.data[d]);
cinbin.push({ 'cid': c0, 'distance': dis });
}
var minResult = cinbin.reduce((cid, obj) => {
return obj.distance < cid.distance ? obj : cid;
});
points0.push({ 'id': d, 'datapoint': this.data[d], 'centroid': minResult.cid });
}
// Assign observations their appropriate centroid.
var centroidBin = [];
for (var c = 0; c < this.k; c++) {
var cb = [];
for (var p = 0; p < points0.length; p++) {
if (c === points0[p].centroid) {
cb.push(points0[p]);
}
}
centroidBin.push(cb);
}
// Calculate the mean distance between centroids and its assigned observations.
this.centroids = [];
for (var bin = 0; bin < centroidBin.length; bin++) {
var xAxis = [],
yAxis = [],
cb0 = centroidBin[bin];
[].forEach.call(cb0, function (dp) {
xAxis.push(dp.datapoint[0]);
yAxis.push(dp.datapoint[1]);
});
var xMean = this.Means(xAxis);
var yMean = this.Means(yAxis);
this.centroids.push([xMean, yMean]);
}
// Test for convergence. If stored centroids equal new centroids then convergence is achieved.
if (JSON.stringify(this.centroids2compare) !== JSON.stringify(this.centroids)) {
this.centroids2compare = [];
points0 = [];
this.clusterCentroids();
}
else {
this.converge(centroidBin, this.centroids);
}
};
window['jsHS']['kmeans2'] = kmeans2;
Implementation
var k50 = new jsi.kmeans2(5, Array50, canvas, function (con, centroids) {
var count50 = 0;
var cmark = {
x: 0,
y: 0,
rad:0,
clr: null,
setArc: function () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.rad, 0, Math.PI * 2, true);
ctx.fillStyle = this.clr;
ctx.fill();
}
};
[].forEach.call(centroids, (c) => {
cmark.x = c[0];
cmark.y = c[1];
cmark.clr = '#0B6623';
cmark.rad = 25;
cmark.setArc();
});
});
This example plots the centroids on a canvas area fine enough but when the browser refreshes the centroids change.
I haven't looked much at your code, but I know that the k-means algorithm tends to give different results when you run it several times. This is because it's highly dependent on where the first centroids (which are selected randomly) are located.
The algorithm can find a local minimum and get "stuck" there, and terminate.
There's no guarantee that you will find the global minimum the first time you run it.
I want to generate a vector of 100 values composed by [1 0]:
This is how I did it in Matlab:
n = 100;
Seq1 = [1 0]; % sequence of 1-0
Vector = repmat(Seq1,(n/2),1); % Creates n/2 sequences of 1-0
The result is a vector like: [1 0 1 0 1 0 1 0...]
Is there a way to get the same result with JavaScript?
You could mimic the function repmat with a while loop.
function repmat(array, count) {
var result = [];
while (count--) {
result = result.concat(array);
}
return result;
}
var nTrials = 100,
Seq1 = [1, 0],
Vector = repmat(Seq1, nTrials / 2);
console.log(Vector);
Assuming you're looking for a way to add a 1 and then a 0, not an array containing 1 and 0:
var myArray = [];
nTrials = 30;
for(i = 1; i<= nTrials/2; i++){
myArray.push(1);
myArray.push(0)
}
document.body.innerHTML = myArray[1];}
https://jsfiddle.net/6seqs6af/1/
FWIW, here is the full repmat implementation in JavaScript.
It uses arrow functions (=>) which isn't available in all browsers.
// Seq1 is an Array (1D vector). We need a Matrix which JavaScript doesn't have
// natively. But we can derive a Matrix type from an Array by adding
// `numberOfRows` and `numberOfColumns` properties as well as a `set` method
function Matrix(numberOfRows, numberOfColumns) {
this.numberOfColumns = numberOfColumns;
this.numberOfRows = numberOfRows;
this.length = numberOfColumns * numberOfRows;
this.fill();
}
Matrix.prototype = Array.prototype;
Matrix.prototype.set = function() {
for (var i = 0; i < arguments.length; i++) {
this[i] = arguments[i];
}
return this;
}
Matrix.prototype.toString = function() {
return this.reduce((acc, x, idx) => acc + (idx % this.numberOfColumns === this.numberOfColumns - 1 ? x + '\n' : x + ', '), '');
}
Matrix.prototype.at = function(row, column) {
return this[row * this.numberOfColumns + column];
}
// Repmap
// ======
function repmat(mat, repeatColumns, repeatRows) {
var numberOfColumns = mat.numberOfColumns * repeatColumns;
var numberOfRows = mat.numberOfRows * repeatRows;
var values = [];
for (var y = 0; y < numberOfRows; y++) {
for (var x = 0; x < numberOfColumns; x++) {
values.push(mat.at(y % mat.numberOfRows, x % mat.numberOfColumns));
}
}
var result = new Matrix(numberOfRows, numberOfColumns);
result.set.apply(result, values);
return result;
}
// Calculation
// ===========
var nTrials = 100;
var seq1 = new Matrix(1, 2);
seq1.set(1, 0);
var vector = repmat(seq1, nTrials / 2, 1);
console.log(vector.toString());
I am continuing to animate a model of a transportation system. The code builds stations, connects them with lines, builds "pods" to run along them, then works out the shortest path between random start and end stations.
jsfiddle
The problems I am having are
a) the pods are traveling one at a time, rather than simultaneously.
b) the pod color is supposed to be related to the number of passengers, but this is clearly not happening.
c) I suspect I am producing a bottleneck in calculating the animation points, as it seems to run more hesitantly than it should.
I would also like to be able to flag the arrival of each pod, so that it can be reloaded, and start a new journey.
Something is wrong with the animation section, but I can't work out what. Any help would be appreciated!
animate();
var lastTime = 0;
var speed = 1; // higher is slower
function animate(time) {
for (var i = 0; i < podArray.length; i++) {
var aPod = podArray[i];
// calculate incremental points along the path
var points = calcWaypoints(aPod.wayStations);
// return if the desired time hasn't elapsed
if ((time - lastTime) < speed) {
requestAnimationFrame(animate);
return;
}
lastTime = time;
//function animate(){
ctx3.clearRect(0, 0, layer3.width, layer3.height);
if (t < points.length - 1) {
requestAnimationFrame(animate);
}
// draw pod from the last waypoint to the current waypoint
ctx3.beginPath();
ctx3.moveTo(aPod.startX, aPod.startY); //(points[t - 1].x, points[t - 1].y);
ctx3.arc(points[t].x, points[t].y, 4, 0, Math.PI * 2, true);
ctx3.fillStyle = aPod.color;
ctx3.fill();
t++;
}
}
a) Your calcWaypoints function takes a parameter that is a list of waystation, and then you ignore that parameter and calculate all points for all pods. This is creating one big array of all points, starting with the first pod's points, then the 2nd's, etc. This is why it starts with the first, then the 2nd then the third.
function calcWaypoints(nextArray) {
var frams = 100;
var waypoints = [];
for (var i = 1; i < nextArray.length; i++) {
var pt0 = nextArray[i - 1];
var pt1 = nextArray[i];
var dx = pt1[0] - pt0[0];
var dy = pt1[1] - pt0[1];
for (var j = 0; j < frams; j++) {
var x = pt0[0] + dx * j / frams //+ dxOff;
var y = pt0[1] + dy * j / frams //+ dyOff;
waypoints.push({
x: x,
y: y
});
};
}
return waypoints;
}
b) This has the same cause as the previous, because you are using the same points for each pod, the pods are written over one another. In addition, you are clearing the canvas between each point. This clear should be moved out of the loop. ctx3.clearRect(0, 0, layer3.width, layer3.height);
c) For some reason you are re-calculating all points with every call to animate. You should calculate them all upfront once and then not in animate. You are also creating way too many calls to requestAnimationFrame in your loop.
I hacked together a few of the above changes and it is working. You should still clean it up a lot more:
layer1 = document.getElementById('layer1');
ctx1 = layer1.getContext('2d');
layer2 = document.getElementById('layer2');
ctx2 = layer2.getContext('2d');
layer3 = document.getElementById('layer3');
ctx3 = layer3.getContext('2d');
window.requestAnimFrame = (function (callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
//STATIONS*************************************
var station = [
['A', 150, 100],
['B', 300, 100],
['C', 200, 175],
['D', 100, 250],
['E', 300, 250],
['G', 400, 250],
['F', 350, 200],
['Airport', 500, 200],
['Central', 500, 350]
];
function draw2() {
for (var i = 0; i < station.length; i++) {
var radius = 10;
ctx2.beginPath();
ctx2.arc(station[i][1], station[i][2], radius, 0, 2 * Math.PI);
ctx2.stroke();
ctx2.fillStyle = 'yellow';
ctx2.fill();
//ADD STATION LETTERS
ctx2.font = '10pt Calibri';
ctx2.fillStyle = 'black';
ctx2.textAlign = 'center';
ctx2.fillText(station[i][0], station[i][1], (station[i][2]) + 4);
}
}
draw2();
//END STATIONS*************************************
//START LINES**************************************
var lineArray = [
['A', 'B', ],
['B', 'A', 'C'],
['C', 'B', 'D', 'E'],
['D', 'C', 'E'],
['E', 'D', 'F', 'G', 'Central'],
['F', 'E', 'G'],
['G', 'F', 'E', 'Airport'],
['Airport', 'G', 'Central'],
['Central', 'E', 'Airport']
];
function drawLines() {
for (m = 0; m < lineArray.length; m++) {
ctx1.lineWidth = 1;
ctx1.beginPath();
for (p = 1; p < lineArray[m].length; p++) {
var startStat = lineArray[m][0];
var stat = lookUp(startStat);
var beginX = station[stat][1];
var beginY = station[stat][2];
ctx1.moveTo(beginX, beginY);
var endStat = lineArray[m][p];
var endSt = lookUp(endStat);
var closeX = station[endSt][1];
var closeY = station[endSt][2];
ctx1.lineTo(closeX, closeY);
}
ctx1.stroke();
}
}
drawLines();
//END LINES*************************************
//SHORTEST PATH*********************************
function Graph() {
var neighbors = this.neighbors = {}; // Key = vertex, value = array of neighbors.
this.addEdge = function (u, v) {
if (neighbors[u] === undefined) { // Add the edge u -> v.
neighbors[u] = [];
}
neighbors[u].push(v);
if (neighbors[v] === undefined) { // Also add the edge v -> u in order
neighbors[v] = []; // to implement an undirected graph.
} // For a directed graph, delete
neighbors[v].push(u); // these four lines.
};
return this;
}
function shortestPath(graph, source, target) {
var stationPath = [];
var coordPath = [];
if (source == target) { // Delete these four lines if
print(source); // you want to look for a cycle
return; // when the source is equal to
} // the target.
var queue = [source],
visited = {
source: true
},
predecessor = {},
tail = 0;
while (tail < queue.length) {
var u = queue[tail++], // Pop a vertex off the queue.
neighbors = graph.neighbors[u];
for (var i = 0; i < neighbors.length; ++i) {
var v = neighbors[i];
if (visited[v]) {
continue;
}
visited[v] = true;
if (v === target) { // Check if the path is complete.
var path = [v]; // If so, backtrack through the path.
while (u !== source) {
path.push(u);
u = predecessor[u];
}
path.push(u);
path.reverse();
print(path.join(' → '));
//console.log('Path: ' + path);
for (s = 1; s < path.length; s++) {
stationPath.push(path[s]);
}
//console.log('Waypoints: ' + stationPath);
for (t = 0; t < stationPath.length; t++) {
staCo = lookUp(stationPath[t]);
staCoX = station[staCo][1];
staCoY = station[staCo][2];
coordPath.push([staCoX, staCoY]);
}
//console.log(coordPath);
return coordPath;
}
predecessor[v] = u;
queue.push(v);
}
}
print('there is no path from ' + source + ' to ' + target);
}
function print(s) { // A quick and dirty way to display output.
s = s || '';
document.getElementById('display').innerHTML += s + '<br>';
}
function findShortestPath(s1, s2) {
var graph = new Graph();
for (w = 0; w < lineArray.length; w++) {
var baseStation = lineArray[w][0];
for (z = 1; z < lineArray[w].length; z++) {
graph.addEdge(baseStation, lineArray[w][z]);
}
}
return (shortestPath(graph, s1, s2));
};
function lookUp(sta) {
//console.log(sta);
for (n = 0; n < station.length; n++) {
if (sta == station[n][0]) {
return n;
break;
}
}
}
//BUILD PODS*************************************
var podArray = [];
function Pod(startX, startY, wayStations, riders, color) {
this.startX = startX;
this.startY = startY;
this.wayStations = wayStations;
this.riders = riders;
this.color = color;
}
var colorArray = ['gold', 'orange', 'red', 'green', 'blue', 'black'];
function randomPass() {
occ = 1 + Math.floor(Math.random() * 6);
return occ;
}
//PROGRAM PODS*********************************************
for (i = 0; i < 3; i++) { //NUMBER OF PODS
//Start Station
var startOff = Math.floor(Math.random() * station.length);
var begSta = station[startOff][0];
var fromX = station[startOff][1];
var fromY = station[startOff][2];
//END STATION
var destNum = Math.floor(Math.random() * station.length);
while (startOff == destNum) {
destNum = Math.floor(Math.random() * station.length);
}
var endSta = station[destNum][0];
function getWayStations(beg, end) {
var fsp = findShortestPath(beg, end);
var nextArray = [];
nextArray.push([fromX, fromY]);
for (var f = 0; f < fsp.length; f++) {
nextArray.push(fsp[f]);
}
return nextArray;
}
//LOAD POD DATA
podArray.push(new Pod(
startX = fromX,
startY = fromY,
wayStations = getWayStations(begSta, endSta),
riders = randomPass(),
color = colorArray[riders - 1]))
}
//END PROGRAM PODS*********************************************
// calc waypoints traveling along nextArray
function calcWaypoints(nextArray) {
var frams = 100;
var waypoints = [];
for (var i = 1; i < nextArray.length; i++) {
var pt0 = nextArray[i - 1];
var pt1 = nextArray[i];
var dx = pt1[0] - pt0[0];
var dy = pt1[1] - pt0[1];
for (var j = 0; j < frams; j++) {
var x = pt0[0] + dx * j / frams //+ dxOff;
var y = pt0[1] + dy * j / frams //+ dyOff;
waypoints.push({
x: x,
y: y
});
};
}
return (waypoints);
}
animate();
var lastTime = 0;
var speed = 1; // higher is slower
function animate(time) {
// return if the desired time hasn't elapsed
if ((time - lastTime) < speed) {
requestAnimationFrame(animate);
return;
}
lastTime = time;
ctx3.clearRect(0, 0, layer3.width, layer3.height);
var callAgain = false;
for (var i = 0; i < podArray.length; i++) {
var aPod = podArray[i];
// calculate incremental points along the path
var points = calcWaypoints(aPod.wayStations);
if (t < points.length - 1) {
callAgain = true;
}
// draw pod from the last waypoint to the current waypoint
if(points.length > t){
ctx3.beginPath();
ctx3.moveTo(aPod.startX, aPod.startY); //(points[t - 1].x, points[t - 1].y);
ctx3.fillStyle = aPod.color;
ctx3.arc(points[t].x, points[t].y, 4, 0, Math.PI * 2, true);
ctx3.fill();
}
}
t++;
if(callAgain) requestAnimationFrame(animate);
}
<canvas id='layer1' style='z-index: 2;
position:absolute;
left:0px;
top:0px;
' height='600px' width='1000'>This text is displayed if your browser does not support HTML5 Canvas.</canvas>
<canvas id='layer2' style='z-index: 3;
position:absolute;
left:0px;
top:0px;
' height='600px' width='1000'>This text is displayed if your browser does not support HTML5 Canvas.</canvas>
<canvas id='layer3' style='z-index: 1;
position:absolute;
left:0px;
top:0px;
' height='600px' width='1000'>This text is displayed if your browser does not support HTML5 Canvas.</canvas>
<div id="display">
</id>