Created
November 18, 2019 11:33
-
-
Save keicoon/b7235f40fff4e924b73a36d2c26734f7 to your computer and use it in GitHub Desktop.
A implementation of finding shortest path on javascript.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Public-domain code by Darel Rex Finley, 2006. | |
// http://alienryderflex.com/shortest_path | |
// javascript code by keicoon, 2019. | |
'use strict'; | |
function pointInPolygonSet(testX, testY, allPolys) { | |
let oddNodes = false; | |
let polyI, i, j; | |
for (polyI = 0; polyI < allPolys.length; ++polyI) { | |
for (i = 0; i < allPolys.poly[polyI].corners; ++i) { | |
j = i + 1; | |
if (j == allPolys.poly[polyI].corners) { | |
j = 0; | |
} | |
if (allPolys.poly[polyI].y[i] < testY && allPolys.poly[polyI].y[j] >= testY || | |
allPolys.poly[polyI].y[i] < testY && allPolys.poly[polyI].y[j] >= testY) { | |
if (allPolys.poly[polyI].x[i] + (testY - allPolys.poly[polyI].y[i]) / | |
(allPolys.poly[polyI].y[j] - allPolys.poly[polyI].y[i]) * | |
(allPolys.poly[polyI].x[j] - allPolys.poly[polyI].x[i]) | |
< testX) { | |
oddNodes = !oddNodes; | |
} | |
} | |
} | |
} | |
return oddNodes; | |
} | |
function lineInPolygonSet(testSX, testSY, testEX, testEY, allPolys) { | |
let theCos, theSin, dist, sX, sY, eX, eY, rotSX, rotSY, rotEX, rotEY, crossX; | |
let i, j, polyI; | |
testEX -= testSX; | |
testEY -= testSY; | |
dist = Math.sqrt(testEX * testEX + testEY * testEY); | |
theCos = testEX / dist; | |
theSin = testEY / dist; | |
for (polyI = 0; polyI < allPolys.length; ++polyI) { | |
for (i = 0; i < allPolys.poly[polyI].corners; ++i) { | |
j = i + 1; | |
if (j == allPolys.poly[polyI].corners) { | |
j = 0; | |
} | |
sX = allPolys.poly[polyI].x[i] - testSX; | |
sY = allPolys.poly[polyI].y[i] - testSY; | |
eX = allPolys.poly[polyI].x[j] - testSX; | |
eY = allPolys.poly[polyI].y[j] - testSY; | |
if (sX == 0 && sY == 0 && eX == testEX && eY == testEY || | |
eX == 0 && eY == 0 && sX == testEX && sY == testEY) { | |
return true; | |
} | |
rotSX = sX * theCos + sY * theSin; | |
rotSY = sY * theCos - sX * theSin; | |
rotEX = eX * theCos + eY * theSin; | |
rotEY = eY * theCos - eX * theSin; | |
if (rotSY < 0 && rotEY > 0 || | |
rotEY < 0 && rotSY > 0) { | |
crossX = rotSX + (rotEX - rotSX) * (0 - rotSY) / (rotEY - rotSY); | |
if (crossX >= 0 && crossX <= dist) { | |
return false | |
} | |
if (rotSY == 0 && rotEY == 0 && | |
(rotSX >= 0 || rotEX >= 0) && | |
(rotSX <= dist || rotEX <= dist) && | |
(rotSX < 0 || rotEX < 0 || rotSX > dist || rotEX > dist)) { | |
return false; | |
} | |
} | |
} | |
} | |
return pointInPolygonSet(testSX + testEX / 2, testSY + testEY / 2, allPolys); | |
} | |
function calcDist(sX, sY, eX, eY) { | |
eX -= sX; | |
eY -= sY; | |
return Math.sqrt(eX * eX + eY * eY); | |
} | |
function swapPoints(a, b) { | |
return [b, a]; | |
} | |
function shortestPath(sX, sY, eX, eY, allPolys) { | |
const INF = Number.MAX_VALUE; | |
let pointList = []; | |
let pointCount; | |
let treeCount, polyI, i, j, bestI, bestJ; | |
let bestDist, newDist; | |
if (!pointInPolygonSet(sX, sY, allPolys) || !pointInPolygonSet(eX, eY, allPolys)) { | |
return { 'state': false }; | |
} | |
if (lineInPolygonSet(sX, sY, eX, eY, allPolys)) { | |
return { 'state': true, 'solutionNodes': [] }; | |
} | |
pointList[0].x = sX; | |
pointList[0].y = sY; | |
pointCount = 1; | |
for (polyI = 0; polyI < allPolys.length; ++polyI) { | |
for (i = 0; i < allPolys.poly[polyI].corners; ++i) { | |
pointList[pointCount].x = allPolys.poly[polyI].x[i]; | |
pointList[pointCount].y = allPolys.poly[polyI].y[i]; | |
++pointCount; | |
} | |
} | |
pointList[pointCount].x = eX; | |
pointList[pointCount].y = eY; | |
++pointCount; | |
treeCount = 1; | |
pointList[0].totalDist = 0; | |
bestJ = 0; | |
while (bestJ < pointCount - 1) { | |
bestDist = INF; | |
for (i = 0; i < treeCount; ++i) { | |
for (j = treeCount; j < pointCount; ++j) { | |
if (lineInPolygonSet(pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, allPolys)) { | |
newDist = pointList[i].totalDist + calcDist(pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y); | |
if (newDist < bestDist) { | |
bestDist = newDist; | |
bestI = i; | |
bestJ = j; | |
} | |
} | |
} | |
} | |
if (bestDist == INF) { | |
return { 'state': false }; | |
} | |
pointList[bestJ].prev = bestI; | |
pointList[bestJ].totalDist = bestDist; | |
[pointList[treeCount], pointList[bestJ]] = swapPoints(pointList[bestJ], pointList[treeCount]); | |
++treeCount; | |
} | |
let solutionNodes = -1; | |
let solutionX = []; | |
let solutionY = []; | |
i = treeCount - 1; | |
while (i > 0) { | |
i = pointLst[i].prev; | |
++solutionNodes; | |
} | |
j = solutionNodes - 1; | |
i = treeCount - 1; | |
while (j > 0) { | |
i = pointList[i].prev; | |
solutionX[j] = pointList[i].x; | |
solutionY[j] = pointList[i].y; | |
--j; | |
} | |
return { 'state': true, solutionNodes, solutionX, solutionY }; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment