2023-04-28 17:35:52 +00:00
|
|
|
--Manage the pathfinding nodes used by DEFCON.
|
|
|
|
--This is important for a mapping tool because the DEFCON client will not load a map unless
|
|
|
|
--the pathfinding nodes form a connected graph.
|
2024-04-25 01:06:41 +00:00
|
|
|
|
2024-07-18 20:00:51 +00:00
|
|
|
local bmp = require 'map.bmp'
|
|
|
|
local locationQuery = require 'map.locationQuery'
|
2023-07-28 23:23:08 +00:00
|
|
|
local lg = assert( love.graphics )
|
2023-04-28 17:35:52 +00:00
|
|
|
|
2024-04-25 01:06:41 +00:00
|
|
|
|
|
|
|
local t = setmetatable({}, {__index = locationQuery})
|
2023-08-28 23:12:43 +00:00
|
|
|
local isSailable
|
|
|
|
|
2024-05-02 00:32:07 +00:00
|
|
|
local function hasEdge( startNode, endNode )
|
2023-08-28 23:12:43 +00:00
|
|
|
local ix, iy, fx, fy = startNode.x, startNode.y, endNode.x, endNode.y
|
|
|
|
if fx < -180 then fx = fx + 180 end
|
|
|
|
if fx > 180 then fx = fx - 180 end
|
2024-04-28 01:38:23 +00:00
|
|
|
|
2023-08-28 23:12:43 +00:00
|
|
|
local dx, dy = fx - ix, fy - iy
|
|
|
|
local mag = math.sqrt( dx * dx + dy * dy )
|
2024-05-02 00:32:07 +00:00
|
|
|
local n = math.floor( mag )--/ 0.5 )
|
|
|
|
dx, dy = dx / mag, dy / mag
|
|
|
|
--dx, dy = 0.5 * dx, 0.5 * dy
|
2024-04-28 01:38:23 +00:00
|
|
|
|
2024-05-02 00:32:07 +00:00
|
|
|
for i = 0, n - 1 do
|
2023-08-28 23:12:43 +00:00
|
|
|
ix, iy = ix + dx, iy + dy
|
2024-04-25 22:29:04 +00:00
|
|
|
if not( isSailable( ix, -iy ) ) then return nil end
|
2023-08-28 23:12:43 +00:00
|
|
|
end
|
2024-04-28 01:38:23 +00:00
|
|
|
|
2023-08-28 23:12:43 +00:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
2024-04-25 01:06:41 +00:00
|
|
|
function t.selectNearest( nodes, x, y )
|
|
|
|
return (nodes.nodes):getClosestPoint( x, y )
|
|
|
|
end
|
|
|
|
|
|
|
|
local travelNode = {}
|
|
|
|
local mtTravelNode = { __index = travelNode }
|
|
|
|
|
|
|
|
function travelNode:formatDisplayInfo()
|
|
|
|
return ([[ TRAVEL NODE: %d
|
|
|
|
LONGITUDE: %3.2f
|
|
|
|
LATITUDE: %3.2f]]):format( self.idx, self.x, self.y )
|
2023-09-04 00:49:02 +00:00
|
|
|
end
|
|
|
|
|
2024-04-28 01:38:23 +00:00
|
|
|
local function worldToBitmap( x, y )
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
local function bitmapToWorld( x, y )
|
2024-05-02 00:32:07 +00:00
|
|
|
local w, a = 360.0, 600.0 / 800.0
|
|
|
|
local h = 360.0 * a
|
|
|
|
x = w * ( x - 800 ) / 800 - w / 2 + 360
|
|
|
|
y = h * ( y - 600 ) / 600 + 180
|
|
|
|
return x, y
|
|
|
|
--(360 * ( 600 / 800 )) * ( y - 600 ) / 600 + 180
|
2024-04-28 01:38:23 +00:00
|
|
|
end
|
|
|
|
|
2023-08-28 23:12:43 +00:00
|
|
|
function t.load( filename, sailable )
|
2024-04-28 01:38:23 +00:00
|
|
|
|
2023-08-28 23:12:43 +00:00
|
|
|
isSailable = sailable
|
2023-08-26 21:05:48 +00:00
|
|
|
local img, imgd = bmp.load( filename )
|
2024-04-29 01:21:32 +00:00
|
|
|
local nodes = { filename = filename, visible = true, nodes = {}, points = {}, connections = {}, img = img }
|
2024-05-02 00:32:07 +00:00
|
|
|
t.nodes = nodes
|
2023-08-28 23:12:43 +00:00
|
|
|
local n = 1
|
2023-08-26 21:05:48 +00:00
|
|
|
for x = 0, 799 do
|
|
|
|
for y = 0, 399 do
|
|
|
|
if imgd:getPixel( x, 399 - y ) > 0 then
|
2024-04-28 01:38:23 +00:00
|
|
|
local long, lat = bitmapToWorld( x, y )
|
|
|
|
nodes.nodes[n] = setmetatable({x = long, y = lat, idx = n}, mtTravelNode )
|
2023-08-28 23:12:43 +00:00
|
|
|
nodes.points[ 2 * n - 1 ] = long
|
|
|
|
nodes.points[ 2 * n ] = lat
|
|
|
|
n = n + 1
|
2023-08-26 21:05:48 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2024-04-28 01:38:23 +00:00
|
|
|
|
2023-08-28 23:12:43 +00:00
|
|
|
for i, srcNode in ipairs( nodes.nodes ) do
|
|
|
|
local adjacent = {}
|
2024-04-28 01:38:23 +00:00
|
|
|
|
2023-08-28 23:12:43 +00:00
|
|
|
for j, destNode in ipairs( nodes.nodes ) do
|
2024-05-02 00:32:07 +00:00
|
|
|
adjacent[j] = hasEdge( srcNode, destNode )
|
2023-08-28 23:12:43 +00:00
|
|
|
end
|
2024-04-28 01:38:23 +00:00
|
|
|
|
2023-08-28 23:12:43 +00:00
|
|
|
nodes.connections[i] = adjacent
|
|
|
|
end
|
2024-05-02 00:32:07 +00:00
|
|
|
|
|
|
|
for i, node in ipairs( nodes.nodes ) do
|
|
|
|
for j, destNode in ipairs( nodes.nodes ) do
|
|
|
|
nodes.connections[i][j] = nodes.connections[i][j] or nodes.connections[j][i]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
nodes.connected = t.isConnected( nodes )
|
2024-04-28 01:38:23 +00:00
|
|
|
|
2024-04-25 01:06:41 +00:00
|
|
|
nodes.nodes = locationQuery.New( nodes.nodes )
|
|
|
|
setmetatable( nodes, {__index = t} )
|
|
|
|
return nodes
|
2023-07-28 05:17:00 +00:00
|
|
|
end
|
2023-04-28 17:35:52 +00:00
|
|
|
|
2024-05-02 00:32:07 +00:00
|
|
|
local function updateAdjacency( connections, i )
|
|
|
|
for j in pairs( connections[i] ) do
|
|
|
|
local edge = hasEdge( i, j )
|
|
|
|
connections[j][i] = edge
|
|
|
|
connections[i][j] = edge
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function dfs( idx, adj, unvisited )
|
|
|
|
if not unvisited[idx] then return end
|
|
|
|
unvisited[ idx ] = nil
|
|
|
|
for i in pairs( adj[idx] ) do dfs( i, adj, unvisited ) end
|
|
|
|
end
|
|
|
|
|
2024-04-21 14:10:53 +00:00
|
|
|
--Determine if graph has more than one connected component.
|
2023-07-28 05:17:00 +00:00
|
|
|
function t.isConnected( nodes )
|
2024-05-02 00:32:07 +00:00
|
|
|
local adj = nodes.connections
|
|
|
|
local unvisited = {}
|
|
|
|
for i in ipairs( nodes.nodes ) do
|
|
|
|
unvisited[i] = true
|
|
|
|
end
|
|
|
|
print( "DEPTH FIRST SEARCH:", "total nodes", #unvisited)
|
2024-04-28 01:38:23 +00:00
|
|
|
|
2024-05-02 00:32:07 +00:00
|
|
|
dfs( 1, adj, unvisited )
|
|
|
|
for k in pairs( unvisited ) do
|
|
|
|
print( "DEPTH FIRST SEARCH:", "unvisited", k)
|
|
|
|
end
|
|
|
|
return not(next(unvisited)) --empty if a graph is connected
|
2023-07-28 05:17:00 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function t.draw( nodes )
|
2024-04-25 01:06:41 +00:00
|
|
|
lg.setPointSize( 8 )
|
2024-05-02 00:32:07 +00:00
|
|
|
lg.setColor( 1, 1, 1, 0.7 )
|
2023-07-28 23:23:08 +00:00
|
|
|
lg.points( nodes.points )
|
2024-04-25 01:06:41 +00:00
|
|
|
return t.drawConnections( nodes )
|
|
|
|
end
|
|
|
|
|
|
|
|
function t.drawConnections( nodes )
|
2024-04-28 01:38:23 +00:00
|
|
|
|
2024-05-02 00:32:07 +00:00
|
|
|
if nodes.connected then
|
|
|
|
lg.setColor( 1, 1, 1, 0.4 )
|
|
|
|
else
|
|
|
|
lg.setColor( 1, 0, 0, 0.7 )
|
|
|
|
end
|
|
|
|
|
2023-08-28 23:12:43 +00:00
|
|
|
for i, connection in pairs( nodes.connections ) do
|
|
|
|
for j in pairs( connection ) do
|
|
|
|
local ix, iy, fx, fy = nodes.nodes[i].x, nodes.nodes[i].y, nodes.nodes[j].x, nodes.nodes[j].y
|
2024-04-28 01:38:23 +00:00
|
|
|
lg.line( ix, iy, fx, fy )
|
2023-08-28 23:12:43 +00:00
|
|
|
end
|
|
|
|
end
|
2024-04-28 01:38:23 +00:00
|
|
|
|
2023-07-28 05:17:00 +00:00
|
|
|
end
|
|
|
|
|
2024-04-29 01:21:32 +00:00
|
|
|
function t.save( nodes )
|
2024-07-13 02:37:50 +00:00
|
|
|
return bmp.travel( nodes.nodes )
|
2023-07-28 05:17:00 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
return t
|