--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. local bmp = require 'bmp' local locationQuery = require 'locationQuery' local lg = assert( love.graphics ) local t = setmetatable({}, {__index = locationQuery}) local isSailable local function hasEdge( startNode, endNode ) 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 local dx, dy = fx - ix, fy - iy local mag = math.sqrt( dx * dx + dy * dy ) local n = math.floor( mag )--/ 0.5 ) dx, dy = dx / mag, dy / mag --dx, dy = 0.5 * dx, 0.5 * dy for i = 0, n - 1 do ix, iy = ix + dx, iy + dy if not( isSailable( ix, -iy ) ) then return nil end end return true end 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 ) end local function worldToBitmap( x, y ) end local function bitmapToWorld( x, y ) 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 end function t.load( filename, sailable ) isSailable = sailable local img, imgd = bmp.load( filename ) local nodes = { filename = filename, visible = true, nodes = {}, points = {}, connections = {}, img = img } t.nodes = nodes local n = 1 for x = 0, 799 do for y = 0, 399 do if imgd:getPixel( x, 399 - y ) > 0 then local long, lat = bitmapToWorld( x, y ) nodes.nodes[n] = setmetatable({x = long, y = lat, idx = n}, mtTravelNode ) nodes.points[ 2 * n - 1 ] = long nodes.points[ 2 * n ] = lat n = n + 1 end end end for i, srcNode in ipairs( nodes.nodes ) do local adjacent = {} for j, destNode in ipairs( nodes.nodes ) do adjacent[j] = hasEdge( srcNode, destNode ) end nodes.connections[i] = adjacent end 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 ) nodes.nodes = locationQuery.New( nodes.nodes ) setmetatable( nodes, {__index = t} ) return nodes end 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 print( "visiting node", idx ) unvisited[ idx ] = nil for i in pairs( adj[idx] ) do dfs( i, adj, unvisited ) end end --Determine if graph has more than one connected component. function t.isConnected( nodes ) local adj = nodes.connections local unvisited = {} for i in ipairs( nodes.nodes ) do unvisited[i] = true end print( "DEPTH FIRST SEARCH:", "total nodes", #unvisited) 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 end function t.draw( nodes ) lg.setPointSize( 8 ) lg.setColor( 1, 1, 1, 0.7 ) lg.points( nodes.points ) return t.drawConnections( nodes ) end function t.drawConnections( nodes ) if nodes.connected then lg.setColor( 1, 1, 1, 0.4 ) else lg.setColor( 1, 0, 0, 0.7 ) end 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 lg.line( ix, iy, fx, fy ) end end end function t.save( nodes ) return bmp.savePoints( nodes.nodes, "800r4") end return t