Impossible things happening, think I'm going crazy

I would really appreciate it if anyone could help me with what’s going on in this code, please…

It is something I’m trying to convert from C for the code exchange, but somewhere in the middle of this listing the values appear to change from what’s in the table to negative values and I just can’t figure out why!

The problem occurs in the ‘calcDist’ function called at line 217…

main.lua:
[lua]-- particle room draw

http://alienryderflex.com/shortest_path/
– turn off status display bar
display.setStatusBar(display.HiddenStatusBar)

– groups
local background, fill, outline, dottop, dostop = display.newGroup(), display.newGroup(), display.newGroup(), display.newGroup(), display.newGroup()

– background
display.newRect(background, 0,0,display.contentWidth,display.contentHeight):setFillColor(30,30,60)

– path start/end points
local startpath, endpath = nil, nil

function findPath(startpath,endpath,polys)
local path = display.newGroup()

print()
print(‘start’,startpath.x,startpath.y)
print(‘end’,endpath.x,endpath.y)
print()
for p=1, #polys do
print('poly '…p)
local poly = polys[p]
for i=1, #poly do
print(poly[i].x,poly[i].y)
end
print()
end
print()

function calcDist( sX, sY, eX, eY )
eX = eX - sX
eY = eY - sY
print(‘LINE’,sX,sY,eX,eY)
local l = display.newLine(sX,sY,eX,eY)
l.width=2
l:setColor(200,200,200)
return math.sqrt( eX * eX + eY * eY )
end

function pointInPolygonSet( testX, testY, allPolys )
local oddNodes = false – bool
local polyI, i, j – int

for polyI=1, #allPolys do
local poly = allPolys[polyI]
for i=1, #poly do
j = i + 1
if (j >= #poly) then
j = 1
end
– print(#poly, tostring(i), tostring(j), tostring(testY), tostring(poly[i]), tostring(poly[j]))
– print(tostring(poly[i].y), tostring(poly[j].y))
if (poly[i].y < testY and poly[j].y >= testY or poly[j].y < testY and poly[i].y >= testY) then
if (poly[i].x + (testY-poly[i].y) / (poly[j].y-poly[i].y) * (poly[j].x-poly[i].x) < testX) then
oddNodes = not oddNodes
end
end
end
end

return oddNodes
end

–[[
// This function should be called with the full set of *all* relevant polygons.
// (The algorithm automatically knows that enclosed polygons are ìno-goî
// areas.)
//
// Note: As much as possible, this algorithm tries to return YES when the
// test line-segment is exactly on the border of the polygon, particularly
// if the test line-segment *is* a side of a polygon.
]]–
function lineInPolygonSet( testSX, testSY, testEX, testEY, allPolys )
local theCos, theSin, dist, sX, sY, eX, eY, rotSX, rotSY, rotEX, rotEY, crossX
local i, j, polyI

testEX = testEX - testSX
testEY = testEY - testSY
dist = math.sqrt( testEX * testEX + testEY * testEY )
theCos = testEX / dist
theSin = testEY / dist

for polyI=1, #allPolys do
local poly = allPolys[polyI]
for i=1, #poly do
j = i + 1
if (j >= #poly) then
j = 1
end

sX = poly[i].x - testSX
sY = poly[i].y - testSY
eX = poly[j].x - testSX
eY = poly[j].y - testSY

if (sX == 0 and sY == 0 and eX == testEX and eY == testEY or eX == 0 and eY == 0 and sX == testEX and sY == testEY) then
return true
end

rotSX = sX * theCos + sY * theSin
rotSY = sY * theCos - sX * theSin
rotEX = eX * theCos + eY * theSin
rotEY = eY * theCos - eX * theSin

if (rotSY < 0 and rotEY > 0 or rotEY < 0 and rotSY > 0) then
crossX = rotSX + (rotEX-rotSX)*(0-rotSY)/(rotEY-rotSY)
if (crossX >= 0 and crossX <= dist) then
return false
end
end

if (rotSY == 0 and rotEY == 0 and (rotSX >= 0 or rotEX >= 0) and (rotSX <= dist or rotEX <= dist)
and (rotSX < 0 or rotEX < 0 or rotSX > dist or rotEX > dist)) then
return false
end
end
end

return pointInPolygonSet( testSX + testEX / 2, testSY + testEY / 2, allPolys )
end

–[[
function swapPoints(point *a, point *b)
point swap=*a
*a=*b
*b=swap
end
]]–

–[[
// Finds the shortest path from sX,sY to eX,eY that stays within the polygon set.
//
// Note: To be safe, the solutionX and solutionY arrays should be large enough
// to accommodate all the corners of your polygon set (although it is
// unlikely that anywhere near that many elements will ever be needed).
//
// Returns YES if the optimal solution was found, or NO if there is no solution.
// If a solution was found, solutionX and solutionY will contain the coordinates
// of the intermediate nodes of the path, in order. (The startpoint and endpoint
// are assumed, and will not be included in the solution.)
]]–
function shortestPath( sX, sY, eX, eY, allPolys ) – , *solutionX, *solutionY )
local solutionNodes, solutions = 0, {}
local INF = 9999999 – (larger than total solution dist could ever be)
local pointList = {} – (enough for all polycorners plus two)
local pointCount
local treeCount, polyI, i, j, bestI, bestJ – int
local bestDist, newDist – double

– Fail if either the startpoint or endpoint is outside the polygon set.
if (not pointInPolygonSet(sX,sY,allPolys) or not pointInPolygonSet(eX,eY,allPolys)) then
print(‘fail’)
return false
end

– If there is a straight-line solution, return with it immediately.
if (lineInPolygonSet(sX,sY,eX,eY,allPolys)) then
solutionNodes = 0
print(‘solutionNodes’,solutionNodes)
return true
end

– Build a point list that refers to the corners of the
– polygons, as well as to the startpoint and endpoint.
pointList[1] = {x=sX,y=sY,totalDist=0}
pointCount=1

for polyI=1, #allPolys do
local poly = allPolys[polyI]
for i=1, #poly do
pointList[#pointList+1] = {x=poly[i].x,y=poly[i].y,totalDist=0}
print(‘point’,pointList[#pointList].x,pointList[#pointList].y)
– pointCount = pointCount + 1
end
end

pointList[#pointList+1] = {x=eX,y=eY,totalDist=0}
– pointCount = pointCount + 1
pointCount = #pointList

for i=1, #pointList do
local c = display.newCircle(pointList[i].x,pointList[i].y,10)
c:setFillColor(0,0,255,150)
print(‘FINAL’,pointList[i].x,pointList[i].y)
end

– Initialize the shortest-path tree to include just the startpoint.
treeCount = 1
pointList[1].totalDist = 0
print(‘start’, pointList[1].totalDist)

– Iteratively grow the shortest-path tree until it reaches the endpoint
– or until it becomes unable to grow, in which case exit with failure.
bestJ = 1
print(‘pointCount’,pointCount)
while (bestJ <= pointCount) do
bestDist = INF
print(‘go’)
for i=1, treeCount do
for i=1, #pointList do
local c = display.newCircle(pointList[i].x,pointList[i].y,10)
c:setFillColor(0,0,255,150)
print(‘SECOND’,pointList[i].x,pointList[i].y)
end
print(i,treeCount,pointCount)
if (treeCount<=pointCount-1) then
for j=treeCount, pointCount-1 do
local lineIn = lineInPolygonSet( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, allPolys)
–print(‘lineIn’,lineIn)
if (lineIn) then
–print(‘calc’,pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y)
local dist = calcDist( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y )
–print(‘dist’,pointList[i].totalDist,dist)
newDist = pointList[i].totalDist + dist
if (newDist < bestDist) then
–print(‘newDist < bestDist’,newDist, bestDist)
bestDist = newDist
bestI = i
bestJ = j
end
end
end
else
print(‘ARGH’,treeCount,pointCount-1)
end
end
print(‘stop’,bestDist,bestJ)

– this IF always enters and appears to show that there is never a solution

–[[if (bestDist == INF) then
print(‘bestDist, INF’, bestDist, INF, bestJ, pointCount)
return false – (no solution)
end]]–

–print(‘bestJ,bestI’,bestJ,bestI)
pointList[bestJ].prev = bestI
pointList[bestJ].totalDist = bestDist

– swapPoints( &pointList[bestJ], &pointList[treeCount] )
local a, b = pointList[bestJ], pointList[treeCount]
pointList[bestJ], pointList[treeCount] = b, a

treeCount = treeCount + 1
end

– Load the solution arrays.
solutionNodes = -1
i = treeCount - 1

while (i > 1) do
i = pointList[i].prev
solutionNodes = solutionNodes + 1
end

j = solutionNodes – - 1
i = treeCount – - 1
print(j,i,solutionNodes,treeCount)
while (j >= 1) do
i = pointList[i].prev
– solutionX[j] = pointList[i].x
– solutionY[j] = pointList[i].y
solutions[#solution+1] = { x=pointList[i].x, y=pointList[i].y }
j = j - 1
end

– Success.
return true, solutions
end

local result, points = shortestPath( startpath.x, startpath.y, endpath.x, endpath.y, polys )
print(result, points)
return result, points
end

local polys = { {} }

function completePoly(event)
local poly = polys[#polys]
poly[#poly+1] = poly[1]
local l = display.newLine(poly[#poly-1].x,poly[#poly-1].y,poly[#poly].x,poly[#poly].y)
l.width=5
event.target:removeEventListener(“tap”,completePoly)
polys[#polys+1] = {}
return true
end

function tap(event)
local poly = polys[#polys]
poly[#poly+1] = event
if (#poly == 1) then
local circle = display.newCircle(event.x,event.y,15)
circle:addEventListener(“tap”,completePoly)
end
if (#poly>1) then
local l = display.newLine(poly[#poly-1].x,poly[#poly-1].y,poly[#poly].x,poly[#poly].y)
l.width=5
end
return true
end
Runtime:addEventListener(“tap”,tap)

function getStartEnd(event)
local c = display.newCircle(event.x,event.y,15)
if (startpath == nil) then
startpath = event
c:setFillColor(0,255,0)
else
endpath = event
c:setFillColor(255,0,0)
table.remove(polys,#polys)
local result, path = findPath(startpath,endpath,polys)
end
return true
end

local stopit = display.newCircle(dostop,50,50,25)
function dostoptap(event)
stopit:setFillColor(0,0,255)
Runtime:removeEventListener(“tap”,tap)
Runtime:addEventListener(“tap”,getStartEnd)
return true
end
dostop:addEventListener(“tap”,dostoptap)[/lua] [import]uid: 8271 topic_id: 24492 reply_id: 324492[/import]

@horacebury

I took a shot at your problem, and here is something I found.

Because you have commented out the lines 236-239, the program keeps running past where it wants to end.

Consequently the variable “treeCount” gets incremented to 11. Then when you do the swap at line 247, pointList[bestJ] gets loaded from “b” which is a nil since pointList[11] is nil.

Does this help? [import]uid: 23636 topic_id: 24492 reply_id: 99263[/import]

Crikey, I didn’t think anyone was going to reply - thank you for taking a look.

I did notice that and corrected it earlier today (it was extremely late for me when I posted the code) and I also corrected a number of other parts. For example, it looks like the calcDist() function was not calculating the distance between the points properly (basic hypoteneuse!)

There’s been plenty of other points which have lead me up the garden path (and further) today, so it is extremely irritating to find that lots of people have converted this guy’s code into other languages with apparent success. (More galling that he has not even compiled it to see if it works - though it evidently does!)

Anyway, thank you for taking a look and could I be so bold as to ask that you take a look at this rather cleaner version, please?

My test process is to tap the points of a triangle, then tap a smaller triangle inside the first, then hit the white circle at the top left and finally place a green and red (start and finish) point in between the two triangles so as to calculate a path around the inner one.

In the listing below is another useful code link, derived from the first, at line 4

You help is really very, very much appreciated.

Current, better but still failing, code:

main.lua:
[lua]-- particle room draw

http://alienryderflex.com/shortest_path/
http://code.google.com/p/icecream-sandwich/source/browse/trunk/+icecream-sandwich/src/se/mushroomwars/mapeditor/model/PathFinder.java?spec=svn52&r=52

– turn off status display bar
display.setStatusBar(display.HiddenStatusBar)

– colours
local colours = {
{r=0,g=0,b=0},
{r=255,g=0,b=0},
{r=0,g=255,b=0},
{r=0,g=0,b=255},
{r=255,g=255,b=0},
{r=255,g=0,b=255},
{r=0,g=255,b=255},
{r=150,g=0,b=0},
{r=0,g=150,b=0},
{r=0,g=0,b=150},
{r=150,g=150,b=0},
{r=150,g=0,b=150},
{r=0,g=150,b=150},
{r=150,g=150,b=150},
}

– groups
local background, fill, outline, dottop, dostop = display.newGroup(), display.newGroup(), display.newGroup(), display.newGroup(), display.newGroup()

– background
display.newRect(background, 0,0,display.contentWidth,display.contentHeight):setFillColor(30,30,60)

– path start/end points
local startpath, endpath = nil, nil

function findPath(startpath,endpath,polys)
local path = display.newGroup()

print()
print(‘start’,startpath.x,startpath.y)
print(‘end’,endpath.x,endpath.y)
print()
for p=1, #polys do
print('poly '…p)
local poly = polys[p]
for i=1, #poly do
print(poly[i].x,poly[i].y)
end
print()
end
print()

function calcDist( sX, sY, eX, eY, col )
dX = eX - sX
dY = eY - sY
local l = display.newLine(sX,sY,eX,eY)
l.width=2
l:setColor(col.r,col.g,col.b)
return math.sqrt( dX * dX + dY * dY )
end

function pointInPolygonSet( testX, testY, allPolys )
local oddNodes = false – bool
local polyI, i, j – int

for polyI=1, #allPolys do
local poly = allPolys[polyI]
for i=1, #poly do
j = i + 1
if (j >= #poly) then
j = 1
end
– print(#poly, tostring(i), tostring(j), tostring(testY), tostring(poly[i]), tostring(poly[j]))
– print(tostring(poly[i].y), tostring(poly[j].y))
if (poly[i].y < testY and poly[j].y >= testY or poly[j].y < testY and poly[i].y >= testY) then
if (poly[i].x + (testY-poly[i].y) / (poly[j].y-poly[i].y) * (poly[j].x-poly[i].x) < testX) then
oddNodes = not oddNodes
end
end
end
end

return oddNodes
end

–[[
// This function should be called with the full set of *all* relevant polygons.
// (The algorithm automatically knows that enclosed polygons are ìno-goî
// areas.)
//
// Note: As much as possible, this algorithm tries to return YES when the
// test line-segment is exactly on the border of the polygon, particularly
// if the test line-segment *is* a side of a polygon.
]]–
function lineInPolygonSet( testSX, testSY, testEX, testEY, allPolys )
local theCos, theSin, dist, sX, sY, eX, eY, rotSX, rotSY, rotEX, rotEY, crossX
local i, j, polyI

testEX = testEX - testSX
testEY = testEY - testSY
dist = math.sqrt( testEX * testEX + testEY * testEY )
theCos = testEX / dist
theSin = testEY / dist

for polyI=1, #allPolys do
local poly = allPolys[polyI]
for i=1, #poly do
j = i + 1
if (j >= #poly) then
j = 1
end

sX = poly[i].x - testSX
sY = poly[i].y - testSY
eX = poly[j].x - testSX
eY = poly[j].y - testSY

if (sX == 0 and sY == 0 and eX == testEX and eY == testEY or eX == 0 and eY == 0 and sX == testEX and sY == testEY) then
return true
end

rotSX = sX * theCos + sY * theSin
rotSY = sY * theCos - sX * theSin
rotEX = eX * theCos + eY * theSin
rotEY = eY * theCos - eX * theSin

if (rotSY < 0 and rotEY > 0 or rotEY < 0 and rotSY > 0) then
crossX = rotSX + (rotEX-rotSX)*(0-rotSY)/(rotEY-rotSY)
if (crossX >= 0 and crossX <= dist) then
return false
end
end

if (rotSY == 0 and rotEY == 0 and (rotSX >= 0 or rotEX >= 0) and (rotSX <= dist or rotEX <= dist)
and (rotSX < 0 or rotEX < 0 or rotSX > dist or rotEX > dist)) then
return false
end
end
end

return pointInPolygonSet( testSX + testEX / 2, testSY + testEY / 2, allPolys )
end

–[[
function swapPoints(point *a, point *b)
point swap=*a
*a=*b
*b=swap
end
]]–

–[[
// Finds the shortest path from sX,sY to eX,eY that stays within the polygon set.
//
// Note: To be safe, the solutionX and solutionY arrays should be large enough
// to accommodate all the corners of your polygon set (although it is
// unlikely that anywhere near that many elements will ever be needed).
//
// Returns YES if the optimal solution was found, or NO if there is no solution.
// If a solution was found, solutionX and solutionY will contain the coordinates
// of the intermediate nodes of the path, in order. (The startpoint and endpoint
// are assumed, and will not be included in the solution.)
]]–
function shortestPath( sX, sY, eX, eY, allPolys ) – , *solutionX, *solutionY )
local solutionNodes, solutions = 0, {}
local INF = 99999999 – (larger than total solution dist could ever be)
local pointList = {} – (enough for all polycorners plus two)
local treeCount, polyI, i, j, bestI, bestJ – int
local bestDist, newDist – double

– Fail if either the startpoint or endpoint is outside the polygon set.
if (not pointInPolygonSet(sX,sY,allPolys) or not pointInPolygonSet(eX,eY,allPolys)) then
return false
end

– If there is a straight-line solution, return with it immediately.
if (lineInPolygonSet(sX,sY,eX,eY,allPolys)) then
solutionNodes = 0
return true
end

– Build a point list that refers to the corners of the
– polygons, as well as to the startpoint and endpoint.
pointList[1] = {x=sX,y=sY,prev=0,totalDist=0}

for polyI=1, #allPolys do
local poly = allPolys[polyI]
for i=1, #poly do
pointList[#pointList+1] = {x=poly[i].x,y=poly[i].y,prev=0,totalDist=0}
end
end

pointList[#pointList+1] = {x=eX,y=eY,prev=0,totalDist=0}

for i=1, #pointList do
local c = display.newCircle(pointList[i].x,pointList[i].y,10)
c:setFillColor(50,50,255,255)
end

– Initialize the shortest-path tree to include just the startpoint.
treeCount = 1
pointList[1].totalDist = 0

– Iteratively grow the shortest-path tree until it reaches the endpoint
– or until it becomes unable to grow, in which case exit with failure.
–[[
bestJ = 1
while (bestJ <= #pointList) do
bestDist = INF
for i=1, treeCount do
for j=treeCount, #pointList do
local lineIn = lineInPolygonSet( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, allPolys)
if (lineIn) then
local dist = calcDist( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, colours[bestJ] )
newDist = pointList[i].totalDist + dist
if (newDist < bestDist) then
bestDist = newDist
bestI = i
bestJ = j
end
end
end
end

– this IF always enters and appears to show that there is never a solution
if (bestDist == INF) then
print(bestJ,#solutions,’{’,colours[bestJ].r,colours[bestJ].g,colours[bestJ].b,’}’)
return false – (no solution)
end

pointList[bestJ].prev = bestI
pointList[bestJ].totalDist = bestDist

– swapPoints( &pointList[bestJ], &pointList[treeCount] )
local a, b = pointList[bestJ], pointList[treeCount]
pointList[bestJ], pointList[treeCount] = b, a

treeCount = treeCount + 1
end
]]–

bestJ = 1
while (bestJ < #pointList) do
bestDist = INF
for i = 1, treeCount do
for j = treeCount, #pointList do
if (lineInPolygonSet( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, allPolys )) then
newDist = pointList[i].totalDist + calcDist( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, colours[bestJ] )
if (newDist < bestDist) then
bestDist = newDist
bestI = i
bestJ = j
end
end
end
end
if (bestDist == INF) then
return false
end
pointList[bestJ].prev = bestI
pointList[bestJ].totalDist = bestDist
local tmp = pointList[bestJ]
pointList[bestJ] = pointList[treeCount]
pointList[treeCount] = tmp
treeCount = treeCount + 1
end

print(‘exited while loop’)

– Load the solution arrays.
solutionNodes = 0
i = treeCount - 1

local t = 0
while (i >= 1 and t < 10) do
t = t + 1
i = pointList[i].prev
solutionNodes = solutionNodes + 1
print(i)
end

j = solutionNodes – - 1
i = treeCount – - 1

while (j >= 1) do
i = pointList[i].prev
solutions[#solution+1] = { x=pointList[i].x, y=pointList[i].y }
j = j - 1
end

– Success.
return true, solutions
end

local result, points = shortestPath( startpath.x, startpath.y, endpath.x, endpath.y, polys )
print(result, points)
return result, points
end
function mattsShortest( polygons, startpoint, endpoint )
local visited = {}

function doLinesIntersect()
end

function getListOfPointsNearCurrentPoint()
end

– produce list of points
for i=1, #polygons do
local polygon = polygons[i]
for p=1, #polygon do

end
end
end
local polys = { {} }

function completePoly(event)
local poly = polys[#polys]
poly[#poly+1] = poly[1]
local l = display.newLine(poly[#poly-1].x,poly[#poly-1].y,poly[#poly].x,poly[#poly].y)
l.width=5
event.target:removeEventListener(“tap”,completePoly)
polys[#polys+1] = {}
return true
end

function tap(event)
local poly = polys[#polys]
poly[#poly+1] = event
print(’’,event.x…’, '…event.y)
if (#poly == 1) then
local circle = display.newCircle(event.x,event.y,15)
circle:addEventListener(“tap”,completePoly)
end
if (#poly>1) then
local l = display.newLine(poly[#poly-1].x,poly[#poly-1].y,poly[#poly].x,poly[#poly].y)
l.width=5
end
return true
end
Runtime:addEventListener(“tap”,tap)

function getStartEnd(event)
local c = display.newCircle(event.x,event.y,15)
if (startpath == nil) then
startpath = event
c:setFillColor(0,255,0)
else
endpath = event
c:setFillColor(255,0,0)
table.remove(polys,#polys)
local result, path = findPath(startpath,endpath,polys)
end
return true
end

local stopit = display.newCircle(dostop,50,50,25)
function dostoptap(event)
stopit:setFillColor(0,0,255)
Runtime:removeEventListener(“tap”,tap)
Runtime:addEventListener(“tap”,getStartEnd)
return true
end
dostop:addEventListener(“tap”,dostoptap)[/lua]
[import]uid: 8271 topic_id: 24492 reply_id: 99264[/import]

Yeah, I’ll take a look, it’s an interesting project. But it may take awhile because of other commitments.

I jumped in with it because I wanted a good test case to check out the new debugging facility of CoronaComplete:

http://developer.anscamobile.com/forum/2012/03/11/coronacomplete-ide-debugger-project-manager

Your problem with the corrupted table was just the ticket for that.

My test process was to tap out a big square with the top edge having a triangle (icicle) hanging down from the top. Hit the white circle at the top left and then place a green point to the left of the icicle and a red point to the right of the icicle. [import]uid: 23636 topic_id: 24492 reply_id: 99269[/import]

I just tried your new code. Much better. I’ll dig into it l little tomorrow. [import]uid: 23636 topic_id: 24492 reply_id: 99270[/import]

I made these changes.
Fixed typo in line 287: #solution s/b #solutions
Changed line 283 back to: i = treeCount - 1

Looks like it works. The returned list of solutions were all the same point: x=366, y=518. These are the coordinates of the bottom tip of the icicle in the polygon I described above. I’m assuming that the solutions table should be all the points between the start and the end points. Does it matter that the single point is repeated?

Sorry, but I can’t seem to imbed the screen shot of the polygon. How do you do that? I’ve been trying this tag:

 ![](www.martinapps.com/photos/shortest.png)  

[import]uid: 23636 topic_id: 24492 reply_id: 99387[/import]

Ok, well, I’ve made the changes you noted - I’ve not seen a difference in the output though. Perhaps I’m not recognising the success? I thought it would have been a table of points showing the shortest route from the findPath function…

Anyway, here is the shape I’ve been using - two nested triangles:

My code now includes the polygons and end points I’ve been using as well:

main.lua:
[lua]-- particle room draw

http://alienryderflex.com/shortest_path/
http://code.google.com/p/icecream-sandwich/source/browse/trunk/+icecream-sandwich/src/se/mushroomwars/mapeditor/model/PathFinder.java?spec=svn52&r=52

– turn off status display bar
display.setStatusBar(display.HiddenStatusBar)

– colours
local colours = {
{r=0,g=0,b=0},
{r=255,g=0,b=0},
{r=0,g=255,b=0},
{r=0,g=0,b=255},
{r=255,g=255,b=0},
{r=255,g=0,b=255},
{r=0,g=255,b=255},
{r=150,g=0,b=0},
{r=0,g=150,b=0},
{r=0,g=0,b=150},
{r=150,g=150,b=0},
{r=150,g=0,b=150},
{r=0,g=150,b=150},
{r=150,g=150,b=150},
}

– groups
local background, fill, outline, dottop, dostop = display.newGroup(), display.newGroup(), display.newGroup(), display.newGroup(), display.newGroup()

– background
display.newRect(background, 0,0,display.contentWidth,display.contentHeight):setFillColor(30,30,60)

– path start/end points
local startpath, endpath = nil, nil

function findPath(startpath,endpath,polys)
local path = display.newGroup()

print()
print(‘start’,startpath.x,startpath.y)
print(‘end’,endpath.x,endpath.y)
print()
for p=1, #polys do
print('poly '…p)
local poly = polys[p]
for i=1, #poly do
print(poly[i].x,poly[i].y)
end
print()
end
print()

function calcDist( sX, sY, eX, eY, col )
dX = eX - sX
dY = eY - sY
local l = display.newLine(sX,sY,eX,eY)
l.width=2
l:setColor(col.r,col.g,col.b)
return math.sqrt( dX * dX + dY * dY )
end

function pointInPolygonSet( testX, testY, allPolys )
local oddNodes = false – bool
local polyI, i, j – int

for polyI=1, #allPolys do
local poly = allPolys[polyI]
for i=1, #poly do
j = i + 1
if (j >= #poly) then
j = 1
end
– print(#poly, tostring(i), tostring(j), tostring(testY), tostring(poly[i]), tostring(poly[j]))
– print(tostring(poly[i].y), tostring(poly[j].y))
if (poly[i].y < testY and poly[j].y >= testY or poly[j].y < testY and poly[i].y >= testY) then
if (poly[i].x + (testY-poly[i].y) / (poly[j].y-poly[i].y) * (poly[j].x-poly[i].x) < testX) then
oddNodes = not oddNodes
end
end
end
end

return oddNodes
end

–[[
// This function should be called with the full set of *all* relevant polygons.
// (The algorithm automatically knows that enclosed polygons are ìno-goî
// areas.)
//
// Note: As much as possible, this algorithm tries to return YES when the
// test line-segment is exactly on the border of the polygon, particularly
// if the test line-segment *is* a side of a polygon.
]]–
function lineInPolygonSet( testSX, testSY, testEX, testEY, allPolys )
local theCos, theSin, dist, sX, sY, eX, eY, rotSX, rotSY, rotEX, rotEY, crossX
local i, j, polyI

testEX = testEX - testSX
testEY = testEY - testSY
dist = math.sqrt( testEX * testEX + testEY * testEY )
theCos = testEX / dist
theSin = testEY / dist

for polyI=1, #allPolys do
local poly = allPolys[polyI]
for i=1, #poly do
j = i + 1
if (j >= #poly) then
j = 1
end

sX = poly[i].x - testSX
sY = poly[i].y - testSY
eX = poly[j].x - testSX
eY = poly[j].y - testSY

if (sX == 0 and sY == 0 and eX == testEX and eY == testEY or eX == 0 and eY == 0 and sX == testEX and sY == testEY) then
return true
end

rotSX = sX * theCos + sY * theSin
rotSY = sY * theCos - sX * theSin
rotEX = eX * theCos + eY * theSin
rotEY = eY * theCos - eX * theSin

if (rotSY < 0 and rotEY > 0 or rotEY < 0 and rotSY > 0) then
crossX = rotSX + (rotEX-rotSX)*(0-rotSY)/(rotEY-rotSY)
if (crossX >= 0 and crossX <= dist) then
return false
end
end

if (rotSY == 0 and rotEY == 0 and (rotSX >= 0 or rotEX >= 0) and (rotSX <= dist or rotEX <= dist)
and (rotSX < 0 or rotEX < 0 or rotSX > dist or rotEX > dist)) then
return false
end
end
end

return pointInPolygonSet( testSX + testEX / 2, testSY + testEY / 2, allPolys )
end

–[[
function swapPoints(point *a, point *b)
point swap=*a
*a=*b
*b=swap
end
]]–

–[[
// Finds the shortest path from sX,sY to eX,eY that stays within the polygon set.
//
// Note: To be safe, the solutionX and solutionY arrays should be large enough
// to accommodate all the corners of your polygon set (although it is
// unlikely that anywhere near that many elements will ever be needed).
//
// Returns YES if the optimal solution was found, or NO if there is no solution.
// If a solution was found, solutionX and solutionY will contain the coordinates
// of the intermediate nodes of the path, in order. (The startpoint and endpoint
// are assumed, and will not be included in the solution.)
]]–
function shortestPath( sX, sY, eX, eY, allPolys ) – , *solutionX, *solutionY )
local solutionNodes, solutions = 0, {}
local INF = 99999999 – (larger than total solution dist could ever be)
local pointList = {} – (enough for all polycorners plus two)
local treeCount, polyI, i, j, bestI, bestJ – int
local bestDist, newDist – double

– Fail if either the startpoint or endpoint is outside the polygon set.
if (not pointInPolygonSet(sX,sY,allPolys) or not pointInPolygonSet(eX,eY,allPolys)) then
return false
end

– If there is a straight-line solution, return with it immediately.
if (lineInPolygonSet(sX,sY,eX,eY,allPolys)) then
solutionNodes = 0
return true
end

– Build a point list that refers to the corners of the
– polygons, as well as to the startpoint and endpoint.
pointList[1] = {x=sX,y=sY,prev=0,totalDist=0}

for polyI=1, #allPolys do
local poly = allPolys[polyI]
for i=1, #poly do
pointList[#pointList+1] = {x=poly[i].x,y=poly[i].y,prev=0,totalDist=0}
end
end

pointList[#pointList+1] = {x=eX,y=eY,prev=0,totalDist=0}

for i=1, #pointList do
local c = display.newCircle(pointList[i].x,pointList[i].y,10)
c:setFillColor(50,50,255,255)
end

– Initialize the shortest-path tree to include just the startpoint.
treeCount = 1
pointList[1].totalDist = 0

– Iteratively grow the shortest-path tree until it reaches the endpoint
– or until it becomes unable to grow, in which case exit with failure.

bestJ = 1
while (bestJ <= #pointList) do
bestDist = INF
for i=1, treeCount do
for j=treeCount, #pointList do
local lineIn = lineInPolygonSet( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, allPolys)
if (lineIn) then
local dist = calcDist( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, colours[bestJ] )
newDist = pointList[i].totalDist + dist
if (newDist < bestDist) then
bestDist = newDist
bestI = i
bestJ = j
end
end
end
end

– this IF always enters and appears to show that there is never a solution
if (bestDist == INF) then
print(bestJ,#solutions,’{’,colours[bestJ].r,colours[bestJ].g,colours[bestJ].b,’}’)
return false – (no solution)
end

pointList[bestJ].prev = bestI
pointList[bestJ].totalDist = bestDist

– swapPoints( &pointList[bestJ], &pointList[treeCount] )
local a, b = pointList[bestJ], pointList[treeCount]
pointList[bestJ], pointList[treeCount] = b, a

treeCount = treeCount + 1
end

–[[
bestJ = 1
while (bestJ <= #pointList) do
bestDist = INF
for i = 1, treeCount do
for j = treeCount, #pointList do
if (lineInPolygonSet( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, allPolys )) then
newDist = pointList[i].totalDist + calcDist( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, colours[bestJ] )
if (newDist < bestDist) then
bestDist = newDist
bestI = i
bestJ = j
end
end
end
end
if (bestDist == INF) then
return false
end
pointList[bestJ].prev = bestI
pointList[bestJ].totalDist = bestDist
local tmp = pointList[bestJ]
pointList[bestJ] = pointList[treeCount]
pointList[treeCount] = tmp
treeCount = treeCount + 1
end
]]–
print(‘exited while loop’)

– Load the solution arrays.
solutionNodes = 0
i = treeCount - 1

local t = 0
while (i >= 1 and t < 10) do
t = t + 1
i = pointList[i].prev
solutionNodes = solutionNodes + 1
print(i)
end

j = solutionNodes – - 1
i = treeCount - 1

while (j >= 1) do
i = pointList[i].prev
solutions[#solutions+1] = { x=pointList[i].x, y=pointList[i].y }
j = j - 1
end

– Success.
return true, solutions
end

local result, points = shortestPath( startpath.x, startpath.y, endpath.x, endpath.y, polys )
print(result, points)
return result, points
end
require(“mathlib”)
function mattsShortest( polygons, startpoint, endpoint )
local visited = {}
local tree = {}

function doLinesIntersect( sa, sb, ea, eb )
end

function getLineOfSightPoints( point, polygons )
local insight = {}

– check line from point to target point

– check each polygon
for p=1, #polygons do
local poly = polygons[p]
for l=1, #poly do
end
doLinesIntersect( polygons[1], polygons )
end

return insight
end

function getShortestTree()

end

return getShortestTree()
end
local polys = { {} }
– uncomment here to see use the shape I’ve been using (two nested triangles)
http://screencast.com/t/C2xPh30A
–[[
polys = {
{ {x=66, y=144} , {x=694, y=150} , {x=344, y=934}, {x=66,y=144} },
{ {x=246, y=272} , {x=506, y=266} , {x=358, y=580}, {x=246,y=272} },
}
startpath, endpath = { x=354,y=172 }, { x=522, y=480 }
local result, path = findPath(startpath,endpath,polys)
]]–

function completePoly(event)
local poly = polys[#polys]
poly[#poly+1] = poly[1]
local l = display.newLine(poly[#poly-1].x,poly[#poly-1].y,poly[#poly].x,poly[#poly].y)
l.width=5
event.target:removeEventListener(“tap”,completePoly)
polys[#polys+1] = {}
return true
end

function tap(event)
local poly = polys[#polys]
poly[#poly+1] = event
print(’’,event.x…’, '…event.y)
if (#poly == 1) then
local circle = display.newCircle(event.x,event.y,15)
circle:addEventListener(“tap”,completePoly)
end
if (#poly>1) then
local l = display.newLine(poly[#poly-1].x,poly[#poly-1].y,poly[#poly].x,poly[#poly].y)
l.width=5
end
return true
end
Runtime:addEventListener(“tap”,tap)

function getStartEnd(event)
local c = display.newCircle(event.x,event.y,15)
if (startpath == nil) then
startpath = event
c:setFillColor(0,255,0)
else
endpath = event
c:setFillColor(255,0,0)
table.remove(polys,#polys)
local result, path = findPath(startpath,endpath,polys)
– local path = mattsShortest(polys,startpath,endpath)
end
return true
end

local stopit = display.newCircle(dostop,50,50,25)
function dostoptap(event)
stopit:setFillColor(0,0,255)
Runtime:removeEventListener(“tap”,tap)
Runtime:addEventListener(“tap”,getStartEnd)
return true
end
dostop:addEventListener(“tap”,dostoptap)[/lua]
[import]uid: 8271 topic_id: 24492 reply_id: 99399[/import]

Not too sure why the image tag would not work for you. Are you writing the tag literally in the body of the post and not surrounding it with anything else? [import]uid: 8271 topic_id: 24492 reply_id: 99400[/import]

I ran with your polygons, and got a path table with 10 points of: x=358, y=580.
I think it is missing a 2nd point of: x=246, y=272 to make a complete path.

I didn’t go into the code far enough to know exactly how the output should have been presented. I’ll try digging into the logic tomorrow.

Here is the table for the shape I used
polys = {
{ {x=136, y=254} ,
{x=302, y=256} ,
{x=344, y=486}, --icicle bottom
{x=452,y=274},
{x=614, y=272} ,
{x=612, y=726} ,
{x=166, y=718},
{x=136,y=254} },
}
startpath, endpath = { x=216,y=362 }, { x=494, y=436 }
local result, path = findPath(startpath,endpath,polys)
This returns a path table with 10 points of: x = 344, y = 486, the icicle bottom point.

This is my shape. The tag works with a jpg, not a png.


[import]uid: 23636 topic_id: 24492 reply_id: 99416[/import]

Hmm, I would have thought that only 3 points would be returned from that shape - its not a long journey… [import]uid: 8271 topic_id: 24492 reply_id: 99454[/import]

Still working on this. Doing some research to understand the code. For instance:

http://en.wikipedia.org/wiki/Breadth-first_search
and
http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

Interesting stuff. [import]uid: 23636 topic_id: 24492 reply_id: 99653[/import]

Here’s where I am so far. It works for my sample case and now it works for your sample case too. It seems to work for any case where the path has just one intermediate point. But it fails on any more complex test case.

I found one error in the C to Lua translation. It’s on line 258 of the new code. I changed some other stuff and rearranged some little stuff for my own testing purposes, so the line numbers don’t correspond anymore. But it’s pretty much the same code as before.

I will continue to work on it as I have the time.

[code]
– particle room draw

http://alienryderflex.com/shortest_path/
http://code.google.com/p/icecream-sandwich/source/browse/trunk/+icecream-sandwich/src/se/mushroomwars/mapeditor/model/PathFinder.java?spec=svn52&r=52

– turn off status display bar
display.setStatusBar(display.HiddenStatusBar)

– colours
local colours = {
{r=0,g=0,b=0},
{r=255,g=0,b=0},
{r=0,g=255,b=0},
{r=0,g=0,b=255},
{r=255,g=255,b=0},
{r=255,g=0,b=255},
{r=0,g=255,b=255},
{r=150,g=0,b=0},
{r=0,g=150,b=0},
{r=0,g=0,b=150},
{r=150,g=150,b=0},
{r=150,g=0,b=150},
{r=0,g=150,b=150},
{r=150,g=150,b=150},
}

– groups
local background, fill, outline, dottop, dostop = display.newGroup(), display.newGroup(), display.newGroup(), display.newGroup(), display.newGroup()

– background
display.newRect(background, 0,0,display.contentWidth,display.contentHeight):setFillColor(30,30,60)

– path start/end points
local startpath, endpath = nil, nil

function findPath(startpath,endpath,polys)
local path = display.newGroup()

print()
print(‘start’,startpath.x,startpath.y)
print(‘end’,endpath.x,endpath.y)
print()
for p=1, #polys do
print('poly '…p)
local poly = polys[p]
for i=1, #poly do
print(poly[i].x,poly[i].y)
end
print()
end
print()

function calcDist( sX, sY, eX, eY, col )
dX = eX - sX
dY = eY - sY
– local l = display.newLine(sX,sY,eX,eY)
– l.width=2
– l:setColor(col.r,col.g,col.b)
return math.sqrt( dX * dX + dY * dY )
end

http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
function pointInPolygonSet( testX, testY, allPolys )
local oddNodes = false – bool
local polyI, i, j – int
for polyI=1, #allPolys do
local poly = allPolys[polyI]
j = #poly
for i=1, #poly do
if ((poly[i].y > testY) ~= (poly[j].y > testY)) then
if (testX < (poly[j].x - poly[i].x) * (testY - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x) then
oddNodes = not oddNodes
end
end
j = i
end
end
return oddNodes
end
– This function should be called with the full set of *all* relevant polygons.
– (The algorithm automatically knows that enclosed polygons are “no-go”
– areas.)

– Note: As much as possible, this algorithm tries to return YES when the
– test line-segment is exactly on the border of the polygon, particularly
– if the test line-segment *is* a side of a polygon.

function lineInPolygonSet( testSX, testSY, testEX, testEY, allPolys )
local theCos, theSin, dist, sX, sY, eX, eY, rotSX, rotSY, rotEX, rotEY, crossX
local i, j, polyI

testEX = testEX - testSX
testEY = testEY - testSY
dist = math.sqrt( testEX * testEX + testEY * testEY )
theCos = testEX / dist
theSin = testEY / dist

for polyI=1, #allPolys do
local poly = allPolys[polyI]
for i=1, #poly do
j = i + 1
if (j >= #poly) then
j = 1
end

sX = poly[i].x - testSX
sY = poly[i].y - testSY
eX = poly[j].x - testSX
eY = poly[j].y - testSY

if (sX == 0 and sY == 0 and eX == testEX and eY == testEY or eX == 0 and eY == 0 and sX == testEX and sY == testEY) then
return true
end

rotSX = sX * theCos + sY * theSin
rotSY = sY * theCos - sX * theSin
rotEX = eX * theCos + eY * theSin
rotEY = eY * theCos - eX * theSin

if (rotSY < 0 and rotEY > 0 or rotEY < 0 and rotSY > 0) then
crossX = rotSX + (rotEX-rotSX)*(0-rotSY)/(rotEY-rotSY)
if (crossX >= 0 and crossX <= dist) then
return false
end
end

if (rotSY == 0 and rotEY == 0 and (rotSX >= 0 or rotEX >= 0) and (rotSX <= dist or rotEX <= dist)
and (rotSX < 0 or rotEX < 0 or rotSX > dist or rotEX > dist)) then
return false
end
end
end

return pointInPolygonSet( testSX + testEX / 2, testSY + testEY / 2, allPolys )
end

– function swapPoints(point *a, point *b)
– point swap=*a
– *a=*b
– *b=swap
– end

– Finds the shortest path from sX,sY to eX,eY that stays within the polygon set.

– Note: To be safe, the solutionX and solutionY arrays should be large enough
– to accommodate all the corners of your polygon set (although it is
– unlikely that anywhere near that many elements will ever be needed).

– Returns YES if the optimal solution was found, or NO if there is no solution.
– If a solution was found, solutionX and solutionY will contain the coordinates
– of the intermediate nodes of the path, in order. (The startpoint and endpoint
– are assumed, and will not be included in the solution.)

function shortestPath( sX, sY, eX, eY, allPolys ) – , *solutionX, *solutionY )
local solutionNodes, solutions = 0, {}
local INF = 99999999 – (larger than total solution dist could ever be)
local pointList = {} – (enough for all polycorners plus two)
local treeCount, polyI, i, j, bestI, bestJ – int
local bestDist, newDist – double

– Fail if either the startpoint or endpoint is outside the polygon set.
if (not pointInPolygonSet(sX,sY,allPolys) or not pointInPolygonSet(eX,eY,allPolys)) then
return false
end

– If there is a straight-line solution, return with it immediately.
if (lineInPolygonSet(sX,sY,eX,eY,allPolys)) then
solutionNodes = 0
return true
end

– Build a point list that refers to the corners of the
– polygons, as well as to the startpoint and endpoint.
pointList[1] = {x=sX,y=sY,prev=0,totalDist=0}

for polyI=1, #allPolys do
local poly = allPolys[polyI]
for i=1, #poly do
pointList[#pointList+1] = {x=poly[i].x,y=poly[i].y,prev=0,totalDist=0}
end
end

pointList[#pointList+1] = {x=eX,y=eY,prev=0,totalDist=0}

for i=1, #pointList do
local c = display.newCircle(pointList[i].x,pointList[i].y,8)
c:setFillColor(50,50,255,255)
end

– Initialize the shortest-path tree to include just the startpoint.
treeCount = 1
pointList[1].totalDist = 0
bestJ = 1
while (bestJ < #pointList) do
bestDist = INF
for i = 1, treeCount do
for j = treeCount, #pointList do
if (lineInPolygonSet( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, allPolys )) then
newDist = pointList[i].totalDist + calcDist( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, colours[bestJ] )
if (newDist < bestDist) then
bestDist = newDist
bestI = i
bestJ = j
end
end
end
end
if (bestDist == INF) then
return false – (no solution)
end
pointList[bestJ].prev = bestI
pointList[bestJ].totalDist = bestDist
local tmp = pointList[bestJ]
pointList[bestJ] = pointList[treeCount]
pointList[treeCount] = tmp
treeCount = treeCount + 1
end

print(‘exited while loop’)
print("treeCount = "…treeCount)

–print the final pointList results for debugging --bm–
local m
for m=1, #pointList do
print('point ',m,pointList[m].x, pointList[m].y, pointList[m].prev, pointList[m].totalDist)
end
print()

– Load the solution arrays.
solutionNodes = 0
i = treeCount - 1

local t = 0
local lastI = nil --bm–
while (i > 0 and t < 10) do
t = t + 1
i = pointList[i].prev
if lastI ~= i then --not looping yet --bm–
solutionNodes = solutionNodes + 1
lastI = i --bm–
print(i)
end
end

print("solutionNodes = "…solutionNodes)
print()

j = solutionNodes – - 1
i = treeCount - 1
while (j >= 1) do
i = pointList[i].prev
solutions[j] = { x=pointList[i].x, y=pointList[i].y }
– solutions[#solutions+1] = { x=pointList[i].x, y=pointList[i].y } --bm-- j = j - 1
end

– Success.
return true, solutions
end

local result, points = shortestPath( startpath.x, startpath.y, endpath.x, endpath.y, polys )
print(result, points)
return result, points
end

function mapShortest( points, startpoint, endpoint ) --changed-- --bm–

function plotLine( sX, sY, eX, eY )
local l = display.newLine(sX,sY,eX,eY)
l.width=3
l:setColor(255,255,0)
end

– produce list of points
local printed = nil
table.insert(points, 1, {x=startpoint.x, y=startpoint.y} )
table.insert(points, #points+1, {x=endpoint.x, y=endpoint.y} )
print()
print(“Table of points on shortest path”)
–Logic below is to eliminate duplicate path points,
–even though the path could still be drawn correctly with them
for p=1, #points do
local point = points[p]
if not printed or printed.x ~= point.x and printed.y ~= point.y then
print('point ',p,point.x,point.y)
if printed then --there is prior point
plotLine( printed.x, printed.y, point.x, point.y )
end
end
printed = point
end
print()
end

local polys = { {} }

---- uncomment here to use the shape horacebury has been using (two nested triangles)
---- http://screencast.com/t/C2xPh30A
–polys = {
– { {x=66, y=144} , {x=694, y=150} , {x=344, y=934}, {x=66,y=144} },
– { {x=246, y=272} , {x=506, y=266} , {x=358, y=580}, {x=246,y=272} },
–}
–startpath, endpath = { x=354,y=172 }, { x=522, y=480 }
–local result, path = findPath(startpath,endpath,polys)
–mapShortest( path, startpath, endpath )
----uncomment here to use the shape BM has been using (one hanging icicle)
----http://www.martinapps.com/photos/shortest.jpg
–polys = {
–{ {x=136, y=254} ,
–{x=302, y=256} ,
–{x=344, y=486}, --icicle
–{x=452,y=274},
–{x=614, y=272} ,
–{x=612, y=726} ,
–{x=166, y=718},
–{x=136,y=254} },
–}
–startpath, endpath = { x=216,y=362 }, { x=494, y=436 }
–local result, path = findPath(startpath,endpath,polys)
–mapShortest( path, startpath, endpath )

function completePoly(event)
local poly = polys[#polys]
poly[#poly+1] = poly[1] --close the polygon
local l = display.newLine(poly[#poly-1].x,poly[#poly-1].y,poly[#poly].x,poly[#poly].y)
l.width=5
event.target:removeEventListener(“tap”,completePoly)
polys[#polys+1] = {} --init for next polygon table
return true
end

function tap(event)
local poly = polys[#polys] --get the current polygon’s table
poly[#poly+1] = event
print(’’,event.x…’, '…event.y)
if (#poly == 1) then --polygon’s first point
local circle = display.newCircle(event.x,event.y,15)
circle:addEventListener(“tap”,completePoly)
end
if (#poly>1) then
local l = display.newLine(poly[#poly-1].x,poly[#poly-1].y,poly[#poly].x,poly[#poly].y)
l.width=5
end
return true
end
Runtime:addEventListener(“tap”,tap)

function getStartEnd(event)
local c = display.newCircle(event.x,event.y,15)
if (startpath == nil) then
startpath = event
c:setFillColor(0,255,0)
else
endpath = event
c:setFillColor(255,0,0)
table.remove(polys,#polys) --no more polygons expected
local result, path = findPath(startpath,endpath,polys)
mapShortest( path, startpath, endpath )
end
return true
end

local stopit = display.newCircle(dostop,50,50,25)
function dostoptap(event)
stopit:setFillColor(0,0,255)
Runtime:removeEventListener(“tap”,tap)
Runtime:addEventListener(“tap”,getStartEnd)
return true
end
dostop:addEventListener(“tap”,dostoptap)
[/code] [import]uid: 23636 topic_id: 24492 reply_id: 100194[/import]

Thank you for your help here - have you tried converting the original source directly yourself? [import]uid: 8271 topic_id: 24492 reply_id: 100231[/import]

I have only done a side-by-side proof reading scan, which found that one error. I’m not a mathematician, so I don’t know the underlying topological procedures represented by the code. But I’m trying to follow the actual working of the code to see if I find what seems to be wrong. Since the C code author didn’t compile it to run and test it himself, there very well may be a logic bug lurking there (as we coders are all too aware of). Here is the error which happens with the next level of complexity:

I’m adding test code to help me see the workings. Add this code to your main to follow along:

[code]
function drawPolygons( startpoint, endpoint, allPolys ) --testing-- --bm–
local polyI, i, j

function plotLine( sX, sY, eX, eY )
local l = display.newLine(sX,sY,eX,eY)
l.width=4
end

local c1 = display.newCircle(startpoint.x,startpoint.y,15)
c1:setFillColor(0,255,0)
local c2 = display.newCircle(endpoint.x,endpoint.y,15)
c2:setFillColor(255,0,0)
for polyI=1, #allPolys do
local poly = allPolys[polyI]
local c3 = display.newCircle(poly[1].x, poly[1].y,15)
c3:setFillColor(255,255,255)
for i=1, #poly-1 do
j = i + 1
plotLine( poly[i].x, poly[i].y, poly[j].x, poly[j].y )
end
end
end

–uncomment here to use the 2nd shape BM has been using (two hanging icicles)
http://www.martinapps.com/photos/shortest.jpg
polys = { {
{x=60, y=200},
{x=172, y=202},
{x=208, y=526}, --icicle
{x=276, y=214},
{x=346, y=218},
{x=378, y=496}, --icicle
{x=440, y=224},
{x=648, y=236},
{x=284, y=912},
{x=60, y=200},
}, }
startpath, endpath = { x=132,y=300 }, { x=452, y=422 }
drawPolygons(startpath,endpath,polys)
local result, path = findPath(startpath,endpath,polys)
mapShortest( path, startpath, endpath )
[/code] [import]uid: 23636 topic_id: 24492 reply_id: 100328[/import]

Hey horacebury,

I think I fixed the code! It was fun learning about Dijkstra’s shortest path algorithm. It is just amazing what you can learn from Google just sitting alone at home. Here are some links:

http://renaud.waldura.com/doc/java/dijkstra/
http://en.wikipedia.org/wiki/Dijkstra's_algorithm

I think that the C code that was your source is correct and would actually run if compiled. However, the key part missing from that source was how the input array “allPolys” should be initialized. What was not understood was what every node’s initial distance value should be: set it to zero for the initial node only and to infinity for all other nodes.
See lines 184-192

I changed your code to not use the final click to enter another node, since that created an unwanted duplicate of the first node.
See lines 419 and 420

And there were a couple of errors indexing into the table:
See lines 107 and 259

Below is the corrected code and an image of a test run.

[code]
– particle room draw

http://alienryderflex.com/shortest_path/
http://code.google.com/p/icecream-sandwich/source/browse/trunk/+icecream-sandwich/src/se/mushroomwars/mapeditor/model/PathFinder.java?spec=svn52&r=52

– turn off status display bar
display.setStatusBar(display.HiddenStatusBar)

– colours
local colours = {
{r=0,g=0,b=0},
{r=255,g=0,b=0},
{r=0,g=255,b=0},
{r=0,g=0,b=255},
{r=255,g=255,b=0},
{r=255,g=0,b=255},
{r=0,g=255,b=255},
{r=150,g=0,b=0},
{r=0,g=150,b=0},
{r=0,g=0,b=150},
{r=150,g=150,b=0},
{r=150,g=0,b=150},
{r=0,g=150,b=150},
{r=150,g=150,b=150},
}

– groups
local background, fill, outline, dottop, dostop = display.newGroup(), display.newGroup(), display.newGroup(), display.newGroup(), display.newGroup()

– background
display.newRect(background, 0,0,display.contentWidth,display.contentHeight):setFillColor(30,30,60)

– path start/end points
local startpath, endpath = nil, nil

function findPath(startpath,endpath,polys)
local path = display.newGroup()

print()
print(‘start’,startpath.x,startpath.y)
print(‘end’,endpath.x,endpath.y)
print()
for p=1, #polys do
print('poly '…p)
local poly = polys[p]
for i=1, #poly do
print(poly[i].x,poly[i].y)
end
print()
end
print()

function calcDist( sX, sY, eX, eY, col )
dX = eX - sX
dY = eY - sY
– local l = display.newLine(sX,sY,eX,eY)
– l.width=2
– l:setColor(col.r,col.g,col.b)
return math.sqrt( dX * dX + dY * dY )
end

http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
function pointInPolygonSet( testX, testY, allPolys )
local oddNodes = false – bool
local polyI, i, j – int
for polyI=1, #allPolys do
local poly = allPolys[polyI]
j = #poly
for i=1, #poly do
if ((poly[i].y > testY) ~= (poly[j].y > testY)) then
if (testX < (poly[j].x - poly[i].x) * (testY - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x) then
oddNodes = not oddNodes
end
end
j = i
end
end
return oddNodes
end
– This function should be called with the full set of *all* relevant polygons.
– (The algorithm automatically knows that enclosed polygons are “no-go”
– areas.)

– Note: As much as possible, this algorithm tries to return YES when the
– test line-segment is exactly on the border of the polygon, particularly
– if the test line-segment *is* a side of a polygon.

function lineInPolygonSet( testSX, testSY, testEX, testEY, allPolys, debug )
local theCos, theSin, dist, sX, sY, eX, eY, rotSX, rotSY, rotEX, rotEY, crossX
local i, j, polyI
local debug = debug or false --bm–
local origTestEX, origTestEY = testEX, testEY

testEX = testEX - testSX
testEY = testEY - testSY
dist = math.sqrt( testEX * testEX + testEY * testEY )
theCos = testEX / dist
theSin = testEY / dist

for polyI=1, #allPolys do
local poly = allPolys[polyI]
for i=1, #poly do
j = i + 1
if (j > #poly) then --bm–
– if (j >= #poly) then --this is TRUE too early–
j = 1
end

sX = poly[i].x - testSX
sY = poly[i].y - testSY
eX = poly[j].x - testSX
eY = poly[j].y - testSY

if (sX == 0 and sY == 0 and eX == testEX and eY == testEY or eX == 0 and eY == 0 and sX == testEX and sY == testEY) then
return true
end

rotSX = sX * theCos + sY * theSin
rotSY = sY * theCos - sX * theSin
rotEX = eX * theCos + eY * theSin
rotEY = eY * theCos - eX * theSin

if (rotSY < 0 and rotEY > 0 or rotEY < 0 and rotSY > 0) then
crossX = rotSX + (rotEX-rotSX)*(0-rotSY)/(rotEY-rotSY)
if (crossX >= 0 and crossX <= dist) then
return false
end
end

if (rotSY == 0 and rotEY == 0 and (rotSX >= 0 or rotEX >= 0) and (rotSX <= dist or rotEX <= dist)
and (rotSX < 0 or rotEX < 0 or rotSX > dist or rotEX > dist)) then
return false
end
end
end

return pointInPolygonSet( testSX + testEX / 2, testSY + testEY / 2, allPolys )
end

– function swapPoints(point *a, point *b)
– point swap=*a
– *a=*b
– *b=swap
– end

– Finds the shortest path from sX,sY to eX,eY that stays within the polygon set.

– Note: To be safe, the solutionX and solutionY arrays should be large enough
– to accommodate all the corners of your polygon set (although it is
– unlikely that anywhere near that many elements will ever be needed).

– Returns YES if the optimal solution was found, or NO if there is no solution.
– If a solution was found, solutionX and solutionY will contain the coordinates
– of the intermediate nodes of the path, in order. (The startpoint and endpoint
– are assumed, and will not be included in the solution.)

function shortestPath( sX, sY, eX, eY, allPolys ) – , *solutionX, *solutionY )
local solutionNodes, solutions = 0, {}
local INF = 99999999 – (larger than total solution dist could ever be)
local pointList = {} – (enough for all polycorners plus two)
local indexCount --bm–
local treeCount, polyI, i, j, bestI, bestJ – int
local bestDist, newDist – double

– Fail if either the startpoint or endpoint is outside the polygon set.
if (not pointInPolygonSet(sX,sY,allPolys) or not pointInPolygonSet(eX,eY,allPolys)) then
return false
end

– If there is a straight-line solution, return with it immediately.
if (lineInPolygonSet(sX,sY,eX,eY,allPolys,false)) then
solutionNodes = 0
return true
end

– Build a point list that refers to the corners of the
– polygons, as well as to the startpoint and endpoint.
– Set the initial totalDist to INF. --bm–
pointList[1] = {x=sX,y=sY,prev=0,totalDist=INF}
for polyI=1, #allPolys do
local poly = allPolys[polyI]
for i=1, #poly do
pointList[#pointList+1] = {x=poly[i].x,y=poly[i].y,prev=0,totalDist=INF}
end
end
pointList[#pointList+1] = {x=eX,y=eY,prev=0,totalDist=INF}

–Mark the display with circles for each pointList object
for i=1, #pointList do
local c = display.newCircle(pointList[i].x,pointList[i].y,8)
c:setFillColor(50,50,255,255)
end

– Initialize the shortest-path tree to include just the startpoint.
treeCount = 1
pointList[1].totalDist = 0
bestJ = 1
while (bestJ < #pointList) do
bestDist = INF
for i = 1, treeCount do
for j = treeCount, #pointList do

if (lineInPolygonSet( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, allPolys, debug )) then
newDist = pointList[i].totalDist + calcDist( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, colours[bestJ] )
if (newDist < bestDist) then
bestDist = newDist
bestI = i
bestJ = j
end
end
end
end
if (bestDist == INF) then
return false – (no solution)
end

pointList[bestJ].prev = bestI
pointList[bestJ].totalDist = bestDist
local tmp = pointList[bestJ]
pointList[bestJ] = pointList[treeCount]
pointList[treeCount] = tmp

treeCount = treeCount + 1
end

print(‘Exited while loop with final pointList results’)

–print the final pointList results for debugging --bm–
local m
for m=1, #pointList do
print('point ',m,pointList[m].x, pointList[m].y, pointList[m].prev, pointList[m].totalDist)
end
print()

– Load the solution arrays.
solutionNodes = - 1 --this initial value will exclude the start point as a solution point
i = treeCount - 1 --start at this index
local nodeLimit = 0
while (i > 1 and nodeLimit < 1000) do --limit solutionNodes count to an arbitrary value
nodeLimit = nodeLimit + 1
i = pointList[i].prev
solutionNodes = solutionNodes + 1
print(i)
end

print("solutionNodes = "…solutionNodes) --this is the number of internal points
print()

j = solutionNodes
i = treeCount - 1
while (j > 0) do
i = pointList[i].prev
solutions[j] = { x=pointList[i].x, y=pointList[i].y }
–solutions[#solutions+1] = { x=pointList[i].x, y=pointList[i].y } j = j - 1
end

– Success.
return true, solutions
end

local result, points = shortestPath( startpath.x, startpath.y, endpath.x, endpath.y, polys )
print(result, points)
return result, points
end

function drawPolygons( startpoint, endpoint, allPolys ) --testing-- --bm–
local polyI, i, j

function plotLine( sX, sY, eX, eY )
local l = display.newLine(sX,sY,eX,eY)
l.width=4
end

local c1 = display.newCircle(startpoint.x,startpoint.y,15)
c1:setFillColor(0,255,0)
local c2 = display.newCircle(endpoint.x,endpoint.y,15)
c2:setFillColor(255,0,0)
for polyI=1, #allPolys do
local poly = allPolys[polyI]
local c3 = display.newCircle(poly[1].x, poly[1].y,15)
c3:setFillColor(255,255,255)
for i=1, #poly-1 do
j = i + 1
plotLine( poly[i].x, poly[i].y, poly[j].x, poly[j].y )
end
plotLine( poly[j].x, poly[j].y, poly[1].x, poly[1].y )
end
end

function mapShortest( points, startpoint, endpoint ) --changed-- --bm–

function plotLine( sX, sY, eX, eY )
local l = display.newLine(sX,sY,eX,eY)
l.width=3
l:setColor(255,255,0)
end

– produce list of points
local points = points or {}
local printed = nil
table.insert(points, 1, {x=startpoint.x, y=startpoint.y} )
table.insert(points, #points+1, {x=endpoint.x, y=endpoint.y} )
print()
print(“Table of points on shortest path”)
–Logic below is to eliminate duplicate path points,
–even though the path could still be drawn correctly with them
for p=1, #points do
local point = points[p]
if not printed or printed.x ~= point.x and printed.y ~= point.y then
print('point ',p,point.x,point.y)
if printed then --there is prior point
plotLine( printed.x, printed.y, point.x, point.y )
end
end
printed = point
end
print()
print(“Shortest path solution points”)
for p=1, #points do
local point = points[p]
print('point ',p,point.x,point.y)
end
print()

end

local polys = { {} }

---- uncomment here to use the shape horacebury has been using (two nested triangles)
---- http://screencast.com/t/C2xPh30A
–polys = {
– { {x=66, y=144} , {x=694, y=150} , {x=344, y=934},
–},
– { {x=246, y=272} , {x=506, y=266} , {x=358, y=580},
–},
–}
–startpath, endpath = { x=354,y=172 }, { x=522, y=480 }
----startpath, endpath = { x=190,y=354 }, { x=522, y=480 }
–drawPolygons(startpath,endpath,polys)
–local result, path = findPath(startpath,endpath,polys)
–if result then mapShortest( path, startpath, endpath ) end

----uncomment here to use the 1st shape BM has been using (one hanging icicle)
----http://www.martinapps.com/photos/shortest.jpg
–polys = {
–{ {x=136, y=254} ,
–{x=302, y=256} ,
–{x=344, y=486}, --icicle
–{x=452,y=274},
–{x=614, y=272} ,
–{x=612, y=726} ,
–{x=166, y=718},
----{x=136,y=254}
–},
–}
–startpath, endpath = { x=216,y=362 }, { x=494, y=436 }
–drawPolygons(startpath,endpath,polys)
–local result, path = findPath(startpath,endpath,polys)
–if result then mapShortest( path, startpath, endpath ) end

----uncomment here to use the 2nd shape BM has been using (two hanging icicles)
----http://www.martinapps.com/photos/shortest.jpg
–polys = { {
–{x=60, y=200},
–{x=172, y=202},
–{x=208, y=526}, --icicle
–{x=276, y=214},
–{x=346, y=218},
–{x=378, y=496}, --icicle
–{x=440, y=224},
–{x=648, y=236},
–{x=284, y=912},
----{x=60, y=200},
–}, }
–startpath, endpath = { x=132,y=300 }, { x=452, y=422 }
–drawPolygons(startpath,endpath,polys)
–local result, path = findPath(startpath,endpath,polys)
–if result then mapShortest( path, startpath, endpath ) end
----uncomment here to use the 3rd shape BM has been using (two nested triangles)
----http://www.martinapps.com/photos/shortest.jpg
–polys = {
–{ --poly 1
–{x=98, y=250},
–{x=656, y=238},
–{x=704, y=878},
–{x=94, y=898},
----{x=98, y=250},
–},
–{ --poly 2
–{x=236, y=522},
–{x=326, y=392},
–{x=378, y=536},
----{x=236, y=522},
–},
–}
–startpath, endpath = { x=190,y=354 }, { x=528, y=728 }
–drawPolygons(startpath,endpath,polys)
–local result, path = findPath(startpath,endpath,polys)
–if result then mapShortest( path, startpath, endpath ) end

function completePoly(event)
local poly = polys[#polys]

– poly[#poly+1] = poly[1] --close the polygon
– local l = display.newLine(poly[#poly-1].x,poly[#poly-1].y,poly[#poly].x,poly[#poly].y)

local l = display.newLine(poly[1].x,poly[1].y,poly[#poly].x,poly[#poly].y)
l.width=5
event.target:removeEventListener(“tap”,completePoly)
polys[#polys+1] = {} --init for next polygon table
return true
end

function tap(event)
local poly = polys[#polys] --get the current polygon’s table
poly[#poly+1] = event
print(’’,event.x…’, '…event.y)
if (#poly == 1) then --polygon’s first point
local circle = display.newCircle(event.x,event.y,15)
circle:addEventListener(“tap”,completePoly)
end
if (#poly>1) then
local l = display.newLine(poly[#poly-1].x,poly[#poly-1].y,poly[#poly].x,poly[#poly].y)
l.width=5
end
return true
end
Runtime:addEventListener(“tap”,tap)

function getStartEnd(event)
local c = display.newCircle(event.x,event.y,15)
if (startpath == nil) then
startpath = event
c:setFillColor(0,255,0)
else
endpath = event
c:setFillColor(255,0,0)
table.remove(polys,#polys) --no more polygons expected
local result, path = findPath(startpath,endpath,polys)
if result then mapShortest( path, startpath, endpath ) end
end
return true
end

local stopit = display.newCircle(dostop,50,50,25)
function dostoptap(event)
stopit:setFillColor(0,0,255)
Runtime:removeEventListener(“tap”,tap)
Runtime:addEventListener(“tap”,getStartEnd)
return true
end
dostop:addEventListener(“tap”,dostoptap)

WOW! That is Incredible! Thank you so much!

I hate to say it, but I haven’t even thought about this code for a while - I think you should email the original author and tell him about the initialisation values being made more obvious…

I originally intended to post this on the code exchange, but this is definitely your piece now, so I’ll let you do that :slight_smile:

Well Done! and thank you :>

Matt

Ps: Here’s one shape I got from it which obviously works a treat!

[import]uid: 8271 topic_id: 24492 reply_id: 101222[/import]

Spoke too soon.

I think it does work, but here’s a ridiculously tough case which breaks it. Doesn’t stop it working, as long as the polygons don’t cross over themselves.

This image shows two artifacts: Near the end of the path it appears to pass through one polygon, I believe this is because the bottom length crosses the concave corner on the top length. Also, the shortest path between some of the points is via the longest distance to some of the inner points of the spikes.

But it’s still awesome! [import]uid: 8271 topic_id: 24492 reply_id: 101224[/import]

Thanks for giving me another problem to work on :-). I tried to simplify the polygon to make it easier to investigate. It looks like it is caused by the amount of overlap the top and bottom teeth have. Here is a test set of points for you to look at. The 2nd point at x=178 can be switched to use y=638 or y=668. This demonstrates the bug. I’ll look at this when I have time.

--Use this code to use the shape of meshing teeth  
polys = { {  
{x=62, y=184},  
--{x=178, y=638}, --works, swap these two  
{x=178, y=668}, --fails, swap these two  
{x=280, y=180},  
{x=406, y=670},  
{x=464, y=196},  
{x=586, y=568},  
{x=384, y=908},  
{x=288, y=400},  
{x=200, y=886},  
{x=40, y=596},  
}, }  
startpath, endpath = {x=100, y=506}, {x=484, y=504}  
drawPolygons(startpath,endpath,polys)  
local result, path = findPath(startpath,endpath,polys)  
if result then mapShortest( path, startpath, endpath ) end  

The other artifact you showed is, I think, an acceptable result. The shortest path still goes along an edge, through allowable points, and does not actually go through a “hole”. Use the sample code below and alternately use just one of the three points at x=336 to see the result.

--Use this code to test the shape with a point going through a side  
polys = {  
 { --poly 1  
 {x=684, y=224},  
 {x=684, y=624},  
 {x=54, y=624},  
 {x=54, y=224},  
 },  
 { --poly 2  
 {x=232, y=422},  
 {x=336, y=446}, --swap these to see the effect on the path  
-- {x=336, y=456}, --swap these to see the effect on the path  
-- {x=336, y=475}, --swap these to see the effect on the path  
 {x=466, y=404},  
 {x=500, y=422},  
 {x=234, y=466},  
 },  
}  
startpath, endpath = {x=584, y=344}, {x=100, y=506}  
drawPolygons(startpath,endpath,polys)  
local result, path = findPath(startpath,endpath,polys)  
if result then mapShortest( path, startpath, endpath ) end  

[import]uid: 23636 topic_id: 24492 reply_id: 101446[/import]

I completely agree on your second point, and on your first: thank you so much for your hard work - I bottled it, I’m afraid! [import]uid: 8271 topic_id: 24492 reply_id: 101530[/import]

The problem is with the math calculations in the lineInPolygonSet() function. There are several trig calculations and then tests are made against resulting values, some of which are expected to be zero. But the floating point math used by Lua results in these expected zero values having instead a value such as 1.4210854715202e-14, so the comparison to zero fails. I have added a hack to work around this problem by changing any number between -0.00000000001 and 0.00000000001 to zero. This seems to work, but further testing might be required. Please try to break it now. The updated code is below.

[code]

http://alienryderflex.com/shortest_path/
http://code.google.com/p/icecream-sandwich/source/browse/trunk/+icecream-sandwich/src/se/mushroomwars/mapeditor/model/PathFinder.java?spec=svn52&r=52

– turn off status display bar
display.setStatusBar(display.HiddenStatusBar)

– colours
local colours = {
{r=0,g=0,b=0},
{r=255,g=0,b=0},
{r=0,g=255,b=0},
{r=0,g=0,b=255},
{r=255,g=255,b=0},
{r=255,g=0,b=255},
{r=0,g=255,b=255},
{r=150,g=0,b=0},
{r=0,g=150,b=0},
{r=0,g=0,b=150},
{r=150,g=150,b=0},
{r=150,g=0,b=150},
{r=0,g=150,b=150},
{r=150,g=150,b=150},
}

– groups
local background, fill, outline, dottop, dostop = display.newGroup(), display.newGroup(), display.newGroup(), display.newGroup(), display.newGroup()

– background
display.newRect(background, 0,0,display.contentWidth,display.contentHeight):setFillColor(30,30,60)

– path start/end points
local startpath, endpath = nil, nil

function findPath(startpath,endpath,polys)
local path = display.newGroup()

local totalPoints = 2 --count the start and end points
for p=1, #polys do --count all the polygon points
local poly = polys[p]
totalPoints = totalPoints + #poly
end

print()
print(‘point#=1’…’ {x=’…startpath.x…’, y=’…startpath.y…’} start’)
print(‘point#=’…totalPoints…’ {x=’…endpath.x…’, y=’…endpath.y…’} end’)
print()
for p=1, #polys do
print(‘poly ‘…p)
local poly = polys[p]
for i=1, #poly do
print(‘point#=’…(i+1)…’ {x=’…poly[i].x…’, y=’…poly[i].y…’},’) --"{x=656, y=238}," format for use as a test setup
end
print()
end
print()

function calcDist( sX, sY, eX, eY, col )
dX = eX - sX
dY = eY - sY
– local l = display.newLine(sX,sY,eX,eY)
– l.width=2
– l:setColor(col.r,col.g,col.b)
return math.sqrt( dX * dX + dY * dY )
end

http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
function pointInPolygonSet( testX, testY, allPolys )
local oddNodes = false – bool
local polyI, i, j – int
for polyI=1, #allPolys do
local poly = allPolys[polyI]
j = #poly
for i=1, #poly do
if ((poly[i].y > testY) ~= (poly[j].y > testY)) then
if (testX < (poly[j].x - poly[i].x) * (testY - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x) then
oddNodes = not oddNodes
end
end
j = i
end
end
return oddNodes
end
– This function should be called with the full set of *all* relevant polygons.
– (The algorithm automatically knows that enclosed polygons are “no-go”
– areas.)

– Note: As much as possible, this algorithm tries to return YES when the
– test line-segment is exactly on the border of the polygon, particularly
– if the test line-segment *is* a side of a polygon.

function lineInPolygonSet( testSX, testSY, testEX, testEY, allPolys, debug )
local theCos, theSin, dist, sX, sY, eX, eY, rotSX, rotSY, rotEX, rotEY, crossX
local i, j, polyI
local origTestEX, origTestEY = testEX, testEY
local testPositiveZero = 0.00000000001
local testNegativeZero = -0.00000000001

testEX = testEX - testSX
testEY = testEY - testSY
dist = math.sqrt( testEX * testEX + testEY * testEY )
theCos = testEX / dist
theSin = testEY / dist

function getZero(x)
if x >= testNegativeZero and x <= testPositiveZero then
x = 0
end
return x
end

for polyI=1, #allPolys do
local poly = allPolys[polyI]
for i=1, #poly do
j = i + 1
if (j > #poly) then --bm–
j = 1
end

sX = poly[i].x - testSX
sY = poly[i].y - testSY
eX = poly[j].x - testSX
eY = poly[j].y - testSY

if (sX == 0 and sY == 0 and eX == testEX and eY == testEY or eX == 0 and eY == 0 and sX == testEX and sY == testEY) then
return true
end

rotSX = getZero(sX * theCos + sY * theSin)
rotSY = getZero(sY * theCos - sX * theSin)
rotEX = getZero(eX * theCos + eY * theSin)
rotEY = getZero(eY * theCos - eX * theSin)
crossX = getZero(rotSX + (rotEX-rotSX)*(0-rotSY)/(rotEY-rotSY))

if (rotSY < 0 and rotEY > 0 or rotEY < 0 and rotSY > 0) then
if (crossX >= 0 and crossX <= dist) then
return false
end
end

if (rotSY == 0 and rotEY == 0 and (rotSX >= 0 or rotEX >= 0) and (rotSX <= dist or rotEX <= dist)
and (rotSX < 0 or rotEX < 0 or rotSX > dist or rotEX > dist)) then
return false
end
end
end

return pointInPolygonSet( testSX + testEX / 2, testSY + testEY / 2, allPolys )
end

– Finds the shortest path from sX,sY to eX,eY that stays within the polygon set.

– Note: To be safe, the solutionX and solutionY arrays should be large enough
– to accommodate all the corners of your polygon set (although it is
– unlikely that anywhere near that many elements will ever be needed).

– Returns YES if the optimal solution was found, or NO if there is no solution.
– If a solution was found, solutionX and solutionY will contain the coordinates
– of the intermediate nodes of the path, in order. (The startpoint and endpoint
– are assumed, and will not be included in the solution.)

function shortestPath( sX, sY, eX, eY, allPolys ) – , *solutionX, *solutionY )
local solutionNodes, solutions = 0, {}
local INF = 99999999 – (larger than total solution dist could ever be)
local pointList = {} – (enough for all polycorners plus two)
local treeCount, polyI, i, j, bestI, bestJ – int
local bestDist, newDist – double

– Fail if either the startpoint or endpoint is outside the polygon set.
if (not pointInPolygonSet(sX,sY,allPolys) or not pointInPolygonSet(eX,eY,allPolys)) then
return false
end

– If there is a straight-line solution, return with it immediately.
if (lineInPolygonSet(sX,sY,eX,eY,allPolys,false)) then
solutionNodes = 0
return true
end

– Build a point list that refers to the corners of the
– polygons, as well as to the startpoint and endpoint.
– Set the initial totalDist to INF. --bm–
pointList[1] = {x=sX,y=sY,prev=0,totalDist=INF}
for polyI=1, #allPolys do
local poly = allPolys[polyI]
for i=1, #poly do
pointList[#pointList+1] = {x=poly[i].x,y=poly[i].y,prev=0,totalDist=INF}
end
end
pointList[#pointList+1] = {x=eX,y=eY,prev=0,totalDist=INF}

–Mark the display with circles for each pointList object
for i=1, #pointList do
local c = display.newCircle(pointList[i].x,pointList[i].y,8)
c:setFillColor(112,204,250,255)
end

– Initialize the shortest-path tree to include just the startpoint.
treeCount = 1
pointList[1].totalDist = 0
bestJ = 1
while (bestJ < #pointList) do
bestDist = INF
for i = 1, treeCount do
for j = treeCount, #pointList do

if (lineInPolygonSet( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, allPolys, debug )) then
newDist = pointList[i].totalDist + calcDist( pointList[i].x, pointList[i].y, pointList[j].x, pointList[j].y, colours[bestJ] )
if (newDist < bestDist) then
bestDist = newDist
bestI = i
bestJ = j
end
end
end
end
if (bestDist == INF) then
return false – (no solution)
end

pointList[bestJ].prev = bestI
pointList[bestJ].totalDist = bestDist
local tmp = pointList[bestJ]
pointList[bestJ] = pointList[treeCount]
pointList[treeCount] = tmp

treeCount = treeCount + 1
end

print(‘Exited while loop with final pointList results’)

–print the final pointList results for debugging --bm–
local m
for m=1, #pointList do
print('point ',m,pointList[m].x, pointList[m].y, pointList[m].prev, pointList[m].totalDist)
end
print()

– Load the solution arrays.
solutionNodes = - 1 --this initial value will exclude the start point as a solution point
i = treeCount - 1 --start at this index
local nodeLimit = 0
while (i > 1 and nodeLimit < 1000) do --limit solutionNodes count to an arbitrary value
nodeLimit = nodeLimit + 1
i = pointList[i].prev
solutionNodes = solutionNodes + 1
print(i)
end

print("solutionNodes = "…solutionNodes) --this is the number of internal points
print()

j = solutionNodes
i = treeCount - 1
while (j > 0) do
i = pointList[i].prev
solutions[j] = { x=pointList[i].x, y=pointList[i].y }
j = j - 1
end

– Success.
return true, solutions
end

local result, points = shortestPath( startpath.x, startpath.y, endpath.x, endpath.y, polys )
print(result, points)
return result, points
end

function drawPolygons( startpoint, endpoint, allPolys ) --testing-- --bm–
local polyI, i, j

function plotLine( sX, sY, eX, eY )
local l = display.newLine(sX,sY,eX,eY)
l.width=4
end

local c1 = display.newCircle(startpoint.x,startpoint.y,15)
c1:setFillColor(0,255,0)
local c2 = display.newCircle(endpoint.x,endpoint.y,15)
c2:setFillColor(255,0,0)
for polyI=1, #allPolys do
local poly = allPolys[polyI]
local c3 = display.newCircle(poly[1].x, poly[1].y,15)
c3:setFillColor(255,255,255)
for i=1, #poly-1 do
j = i + 1
plotLine( poly[i].x, poly[i].y, poly[j].x, poly[j].y )
end
plotLine( poly[j].x, poly[j].y, poly[1].x, poly[1].y )
end
end

function mapShortest( points, startpoint, endpoint ) --bm–

function plotLine( sX, sY, eX, eY )
local l = display.newLine(sX,sY,eX,eY)
l.width=3
l:setColor(255,255,0)
end

– produce list of points
local points = points or {}
local printed = nil
table.insert(points, 1, {x=startpoint.x, y=startpoint.y} )
table.insert(points, #points+1, {x=endpoint.x, y=endpoint.y} )
print()
print(“Table of points on shortest path”)
–Logic below is to eliminate duplicate path points,
–even though the path could still be drawn correctly with them
for p=1, #points do
local point = points[p]
if not printed or printed.x ~= point.x and printed.y ~= point.y then
print('point ',p,point.x,point.y)
if printed then --there is prior point
plotLine( printed.x, printed.y, point.x, point.y )
end
end
printed = point
end
print()
end

local polys = { {} }

---- uncomment here to use the shape horacebury has been using (two nested triangles)
---- http://screencast.com/t/C2xPh30A
–polys = {
– { {x=66, y=144} , {x=694, y=150} , {x=344, y=934},
–},
– { {x=246, y=272} , {x=506, y=266} , {x=358, y=580},
–},
–}
–startpath, endpath = { x=354,y=172 }, { x=522, y=480 }
----startpath, endpath = { x=190,y=354 }, { x=522, y=480 }
–drawPolygons(startpath,endpath,polys)
–local result, path = findPath(startpath,endpath,polys)
–if result then mapShortest( path, startpath, endpath ) end

----uncomment here to use the 1st shape BM has been using (one hanging icicle)
–polys = {
–{ {x=136, y=254} ,
–{x=302, y=256} ,
–{x=344, y=486}, --icicle
–{x=452,y=274},
–{x=614, y=272} ,
–{x=612, y=726} ,
–{x=166, y=718},
–},
–}
–startpath, endpath = { x=216,y=362 }, { x=494, y=436 }
–drawPolygons(startpath,endpath,polys)
–local result, path = findPath(startpath,endpath,polys)
–if result then mapShortest( path, startpath, endpath ) end

----uncomment here to use the 2nd shape BM has been using (two hanging icicles)
–polys = { {
–{x=60, y=200},
–{x=172, y=202},
–{x=208, y=526}, --icicle
–{x=276, y=214},
–{x=346, y=218},
–{x=378, y=496}, --icicle
–{x=440, y=224},
–{x=648, y=236},
–{x=284, y=912},
–}, }
–startpath, endpath = { x=132,y=300 }, { x=452, y=422 }
–drawPolygons(startpath,endpath,polys)
–local result, path = findPath(startpath,endpath,polys)
–if result then mapShortest( path, startpath, endpath ) end
----uncomment here to use the 3rd shape BM has been using (two nested triangles)
–polys = {
–{ --poly 1
–{x=98, y=250},
–{x=656, y=238},
–{x=704, y=878},
–{x=94, y=898},
–},
–{ --poly 2
–{x=236, y=522},
–{x=326, y=392},
–{x=378, y=536},
–},
–}
–startpath, endpath = { x=190,y=354 }, { x=528, y=728 }
–drawPolygons(startpath,endpath,polys)
–local result, path = findPath(startpath,endpath,polys)
–if result then mapShortest( path, startpath, endpath ) end
----uncomment here to use the 4th shape BM has been using (upper/lower teeth)
----http://www.martinapps.com/photos/shortest4.jpg
–polys = {
– { --poly 1
– {x=28, y=252},
– {x=16, y=126},
– {x=182, y=36},
– {x=262, y=112},
– {x=356, y=38},
– {x=454, y=110},
– {x=686, y=32},
– {x=690, y=102},
– {x=122, y=208},
– {x=114, y=490},
– {x=174, y=264},
– {x=244, y=548},
– {x=288, y=254},
– {x=364, y=564},
– {x=400, y=234},
– {x=514, y=564},
– {x=554, y=206},
– {x=622, y=514},
– {x=690, y=138},
– {x=738, y=936},
– {x=560, y=401},
– {x=524, y=916},
– {x=427, y=400},
– {x=378, y=900},
– {x=300, y=400},
– {x=236, y=886},
– {x=174, y=400},
– {x=44, y=922},
– },
– { --poly 2
– {x=132, y=122},

– {x=236, y=146}, --swap these two to see the effect on the path
---- {x=236, y=156}, --swap these two to see the effect on the path

– {x=366, y=104},
– {x=400, y=122},
– {x=134, y=166},
– },
– }
–startpath, endpath = { x=670,y=400 }, { x=380, y=80 }
–drawPolygons(startpath,endpath,polys)
–local result, path = findPath(startpath,endpath,polys)
–if result then mapShortest( path, startpath, endpath ) end
----uncomment here to use this shape with a point going through a side
–polys = {
– { --poly 1
– {x=684, y=224},
– {x=684, y=624},
– {x=54, y=624},
– {x=54, y=224},
– },
– { --poly 2
– {x=232, y=422},
– --Alternatively swap the following three points
---- {x=336, y=446}, --swap these to see the effect on the path
---- {x=336, y=456}, --swap these to see the effect on the path
– {x=336, y=475}, --swap these to see the effect on the path
– {x=466, y=404},
– {x=500, y=422},
– {x=234, y=466},
– },
–}
----startpath, endpath = {x=336, y=344}, {x=336, y=506}
–startpath, endpath = {x=584, y=344}, {x=100, y=506}
–drawPolygons(startpath,endpath,polys)
–local result, path = findPath(startpath,endpath,polys)
–if result then mapShortest( path, startpath, endpath ) end
----uncomment here to use this shape of one meshing teeth
–polys = { {
–{x=62, y=184},
----{x=178, y=638}, --works
–{x=178, y=668}, --fails
–{x=280, y=180},
–{x=406, y=670},
–{x=464, y=196},
–{x=586, y=568},
–{x=384, y=908},
–{x=288, y=400},
–{x=200, y=886},
–{x=40, y=596},
–}, }
–startpath, endpath = {x=100, y=506}, {x=484, y=504}
–drawPolygons(startpath,endpath,polys)
–local result, path = findPath(startpath,endpath,polys)
–if result then mapShortest( path, startpath, endpath ) end
----uncomment here to use a shape using one (upper/lower teeth)
----http://www.martinapps.com/photos/shortest4.jpg
–polys = {
– { --poly 1
– {x=28, y=252},
– {x=16, y=126},
– {x=686, y=32},
– {x=690, y=102},
– {x=122, y=208},
– {x=114, y=490},
– {x=244, y=548},
– {x=288, y=254},
– {x=364, y=564},
– {x=690, y=138},
– {x=738, y=936},
– {x=378, y=900},
– {x=300, y=400},
– {x=236, y=886},
– {x=44, y=922},
– },
– }
–startpath, endpath = { x=670,y=400 }, { x=380, y=80 }
----startpath, endpath = { x=660,y=440 }, { x=362, y=100 }
–drawPolygons(startpath,endpath,polys)
–local result, path = findPath(startpath,endpath,polys)
–if result then mapShortest( path, startpath, endpath ) end
function completePoly(event)
local poly = polys[#polys]

– poly[#poly+1] = poly[1] --close the polygon
– local l = display.newLine(poly[#poly-1].x,poly[#poly-1].y,poly[#poly].x,poly[#poly].y)

local l = display.newLine(poly[1].x,poly[1].y,poly[#poly].x,poly[#poly].y)
l.width=5
event.target:removeEventListener(“tap”,completePoly)
polys[#polys+1] = {} --init for next polygon table
return true
end

function tap(event)
local poly = polys[#polys] --get the current polygon’s table
poly[#poly+1] = event
– print(’’,event.x…’, '…event.y)
if (#poly == 1) then --polygon’s first point
local circle = display.newCircle(event.x,event.y,15)
circle:addEventListener(“tap”,completePoly)
end
if (#poly>1) then
local l = display.newLine(poly[#poly-1].x,poly[#poly-1].y,poly[#poly].x,poly[#poly].y)
l.width=5
end
return true
end
Runtime:addEventListener(“tap”,tap)

function getStartEnd(event)
local c = display.newCircle(event.x,event.y,15)
if (startpath == nil) then
startpath = event
c:setFillColor(0,255,0)
else
endpath = event
c:setFillColor(255,0,0)
table.remove(polys,#polys) --no more polygons expected
local result, path = findPath(startpath,endpath,polys)
if result then mapShortest( path, startpath, endpath ) end
end
return true
end

local stopit = display.newCircle(dostop,50,50,25)
function dostoptap(event)
stopit:setFillColor(0,0,255)
Runtime:removeEventListener(“tap”,tap)
Runtime:addEventListener(“tap”,getStartEnd)
return true
end
dostop:addEventListener(“tap”,dostoptap)
[/code] [import]uid: 23636 topic_id: 24492 reply_id: 101733[/import]