From: Brendan Hansen Date: Sun, 24 Feb 2019 04:27:09 +0000 (-0600) Subject: working version X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=241f7278221f28dffad702a13e93efefd6dd2515;p=graph-algorithms.git working version --- diff --git a/lua/graph.lua b/lua/graph.lua index 7806791..1e406f4 100644 --- a/lua/graph.lua +++ b/lua/graph.lua @@ -25,6 +25,8 @@ function Graph:addNode(x, y) end function Graph:deleteNode(nodeID) + if nodeID == nil then return end + assert(type(nodeID) == "number") local g = self.graph diff --git a/main.lua b/main.lua index a449603..fb84baf 100644 --- a/main.lua +++ b/main.lua @@ -6,8 +6,11 @@ local graph = nil local regions = nil local graph_region = nil +local GRAPH_FONT, SIDEBAR_FONT + function love.load() GRAPH_FONT = love.graphics.setNewFont(16) + SIDEBAR_FONT = love.graphics.setNewFont(24) graph = Graph:new() @@ -31,6 +34,7 @@ function love.load() -- other properties selectedNode = nil; postDrawFunction = nil; + clickNodeFunction = nil; isMouseDown = false; update = function(self) end; @@ -55,10 +59,15 @@ function love.load() local d = (x - nx) * (x - nx) + (y - ny) * (y - ny) if d <= 20 * 20 then - self.selectedNode = node.id + if self.clickNodeFunction == nil then + self.selectedNode = node.id + else + self.clickNodeFunction(node) + end end end end; + mouseup = function(self, x, y) self.isMouseDown = false end; @@ -68,8 +77,9 @@ function love.load() graph:setNodePos(self.selectedNode, x, y) end end; - mouseenter = function(self) - end; + + mouseenter = function(self) end; + mouseleave = function(self) self.isMouseDown = false end; @@ -77,6 +87,91 @@ function love.load() regions:add(graph_region) + regions:add { + priority = 1; + rect = { 0, 0, 200, 600 }; + + buttons = { + { + text = "Add node", + click = function() + graph:addNode(300, 300) + end; + }; + { + text = "Delete node", + click = function() + graph:deleteNode(graph_region.selectedNode) + end; + }; + { + text = "Add arc", + click = function() + local curr = graph_region.selectedNode + if curr == nil then return end + + graph_region.clickNodeFunction = function(node) + graph:addArc(curr, node.id, 1) + graph_region.clickNodeFunction = nil + end + end; + }; + { + text = "Add edge", + click = function() + local curr = graph_region.selectedNode + if curr == nil then return end + + graph_region.clickNodeFunction = function(node) + graph:addEdge(curr, node.id, 1) + graph_region.clickNodeFunction = nil + end + end; + }; + { + text = "Run Dijkstras", + click = function() + if graph_region.selectedNode ~= nil then + createDijskstraStepper(graph_region.selectedNode) + end + end; + }; + }; + mousePos = -1; + + update = function(self) end; + + draw = function(self) + love.graphics.setFont(SIDEBAR_FONT) + for i, button in ipairs(self.buttons) do + if self.mousePos >= (i - 1) * 60 + 10 and self.mousePos <= i * 60 + 10 then + love.graphics.setColor(.4, .4, .4) + else + love.graphics.setColor(.2, .2, .2) + end + love.graphics.rectangle("fill", 10, (i - 1) * 60 + 10, 180, 40) + + love.graphics.setColor(1, 1, 1) + love.graphics.printf(button.text, 0, (i - 1) * 60 + 15, 200, "center") + end + end; + + mousedown = function(self, x, y) + for i, button in ipairs(self.buttons) do + if y >= (i - 1) * 60 + 10 and y <= i * 60 + 10 then + button.click() + end + end + end; + mouseup = function(self, x, y) end; + mousemove = function(self, x, y, dx, dy) + self.mousePos = y + end; + mouseenter = function(self) end; + mouseleave = function(self) + self.mousePos = -1 + end; + } -- regions:add { -- priority = 0; @@ -92,11 +187,17 @@ function love.load() -- } end -function createDijskstraStepper() +local dijkstrasOpen = false +function createDijskstraStepper(start) + if dijkstrasOpen then return end + + dijkstrasOpen = true local step = -1 + local isDone = false function updateDijkstras() - local dj = graph:dijkstras(0, step) + local dj, done = graph:dijkstras(start, step) + isDone = done graph_region.postDrawFunction = function(reg) drawDijkstras(graph, dj) end @@ -120,15 +221,19 @@ function createDijskstraStepper() mousedown = function(self, x, y) if x >= 265 and x <= 295 then step = step - 1 + isDone = false if step < -1 then step = -1 end elseif x >= 305 and x <= 335 then - step = step + 1 + if not isDone then + step = step + 1 + end end updateDijkstras() if x >= 550 then regions:remove(reg_id) graph_region.postDrawFunction = nil + dijkstrasOpen = false end end; mouseup = function(self, x, y) end; @@ -158,41 +263,48 @@ end -- DRAWING FUNCTIONS +function drawEdge(x1, y1, x2, y2, directed, weight, color) + love.graphics.setColor(color) + + love.graphics.line(x1, y1, x2, y2) + + local cx = math.lerp(x1, x2, 0.5) + local cy = math.lerp(y1, y2, 0.5) + local arrowSize = 22 + + if directed == 2 then + local alpha = math.atan2(x2 - x1, y2 - y1) + + local triangle = { + cx + arrowSize * math.sin(alpha + 0 * math.pi / 3), cy + arrowSize * math.cos(alpha + 0 * math.pi / 3), + cx + arrowSize * math.sin(alpha + 2 * math.pi / 3), cy + arrowSize * math.cos(alpha + 2 * math.pi / 3), + cx + arrowSize * math.sin(alpha + 4 * math.pi / 3), cy + arrowSize * math.cos(alpha + 4 * math.pi / 3) + } + + love.graphics.polygon("fill", triangle) + elseif directed == 1 then + love.graphics.rectangle("fill", cx - arrowSize * .6, cy - arrowSize * .6, arrowSize * 1.2, arrowSize * 1.2) + end + + love.graphics.setColor(1, 1, 1) + love.graphics.printf(tostring(weight), cx - 20, cy - 10, 40, "center") +end + local NODE_INNER_RADIUS = 17 local NODE_OUTER_RADIUS = 20 function drawGraph(graph, selectedNode) local nodes = graph:getNodes() local edges = graph:getEdges() + love.graphics.setFont(GRAPH_FONT) love.graphics.setLineWidth(4) for _, edge in pairs(edges) do - love.graphics.setColor(0, 0, 0) local x1 = nodes[edge.from_node].x local y1 = nodes[edge.from_node].y local x2 = nodes[edge.to_node].x local y2 = nodes[edge.to_node].y - love.graphics.line(x1, y1, x2, y2) - - local cx = math.lerp(x1, x2, 0.5) - local cy = math.lerp(y1, y2, 0.5) - local arrowSize = 22 - - if edge.directed then - local alpha = math.atan2(x2 - x1, y2 - y1) - - local triangle = { - cx + arrowSize * math.sin(alpha + 0 * math.pi / 3), cy + arrowSize * math.cos(alpha + 0 * math.pi / 3), - cx + arrowSize * math.sin(alpha + 2 * math.pi / 3), cy + arrowSize * math.cos(alpha + 2 * math.pi / 3), - cx + arrowSize * math.sin(alpha + 4 * math.pi / 3), cy + arrowSize * math.cos(alpha + 4 * math.pi / 3) - } - - love.graphics.polygon("fill", triangle) - else - love.graphics.rectangle("fill", cx - arrowSize * .6, cy - arrowSize * .6, arrowSize * 1.2, arrowSize * 1.2) - end - love.graphics.setColor(1, 1, 1) - love.graphics.printf(tostring(edge.weight), cx - 20, cy - 10, 40, "center") + drawEdge(x1, y1, x2, y2, (edge.directed and 2 or 1), edge.weight, {0, 0, 0}) end for _, node in pairs(nodes) do @@ -210,10 +322,17 @@ end function drawDijkstras(graph, dijkstras) love.graphics.setFont(GRAPH_FONT) + if dijkstras.from >= 0 and dijkstras.to >= 0 then + local x1, y1 = graph:getNodePos(dijkstras.from) + local x2, y2 = graph:getNodePos(dijkstras.to) + + drawEdge(x1, y1, x2, y2, 0, dijkstras.weight, {1, 0, 0}) + end + for _, node in ipairs(dijkstras) do if node.distance >= 0 then local x, y = graph:getNodePos(node.id) - love.graphics.setColor(0, 1, 0) + love.graphics.setColor(1, 0, 0) love.graphics.circle("fill", x, y, NODE_INNER_RADIUS) love.graphics.setColor(0, 0, 0) diff --git a/modules/graph_algorithms.c b/modules/graph_algorithms.c index fc5aa7e..c4fae9f 100644 --- a/modules/graph_algorithms.c +++ b/modules/graph_algorithms.c @@ -61,8 +61,9 @@ int dijkstras(lua_State *L) insert_dj(shortest_distance, distance_walker->node_id, -1); distance_walker = distance_walker->next; } - int last_from; - int last_to; + int last_from = -1; + int last_to = -1; + int last_weight; while (connected_nodes != NULL && current_iterations <= iterations) { @@ -89,6 +90,7 @@ int dijkstras(lua_State *L) shortest_distance_tmp = distance_to_current_node + edge_walker->weight; from = current_node->node_id; to = edge_walker->to_id; + last_weight = edge_walker->weight; } } edge_walker = edge_walker->next_edge; @@ -142,8 +144,19 @@ int dijkstras(lua_State *L) index++; } + lua_pushnumber(L, last_from); + lua_setfield(L, 1, "from"); + + lua_pushnumber(L, last_to); + lua_setfield(L, 1, "to"); + + lua_pushnumber(L, last_weight); + lua_setfield(L, 1, "weight"); + + lua_pushboolean(L, connected_nodes == NULL); + //put shorest_distance linked list into table and return table delete_dj(shortest_distance); - return 1; + return 2; } diff --git a/modules/graph_standard.c b/modules/graph_standard.c index 5c287b3..d3f6c3f 100644 --- a/modules/graph_standard.c +++ b/modules/graph_standard.c @@ -67,7 +67,6 @@ int del_node(lua_State *L) header head = (header)lua_touserdata(L,1); int nid = lua_tointeger(L,2); - //find the node connect walker = head->front; connect follower = walker; @@ -108,14 +107,17 @@ int del_node(lua_State *L) if (edge_walker == node_walker->neighbor_list) { node_walker->neighbor_list = node_walker->neighbor_list->next_edge; + free(edge_walker); + edge_walker = node_walker->neighbor_list; + delete_walker = edge_walker; } else { - delete_walker->next_edge = edge_walker; + delete_walker->next_edge = edge_walker->next_edge; delete_walker = delete_walker->next_edge; + edge_walker = edge_walker->next_edge; + free(delete_walker); } - edge_walker = edge_walker->next_edge; - free(delete_walker); } else { @@ -126,7 +128,6 @@ int del_node(lua_State *L) node_walker = node_walker->next_node; } - //delete the node if (walker == head->front) {