Exemplo n.º 1
0
def loadData():
    dirs = os.listdir(dataFilePath)
    for classPathName, classIndex in zip(dirs, range(len(dirs))):
        for graphPathName in os.listdir(dataFilePath + classPathName):
            graph = Graph(dataFilePath + classPathName + "/" + graphPathName,
                          priorList, "#")
            graphList.append(graph)
            labelList.append(classIndex)
Exemplo n.º 2
0
    def __init__(self, parent=None):
        super(AppWidget, self).__init__(parent)
        self.graph = Graph()
        self.mode = '+ .'
        self._next_label = 1

        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.searchLoop)

        self.nodeR = 5
        self.nodeDetectPadding = 7
        self.nodeSpacing = 30

        self.edgeDetectPadding = 3

        self.clearSearchVars()

        self.setupUi()
Exemplo n.º 3
0
    def __init__(self,
                 width=640,
                 height=400,
                 node_r=5,
                 node_padding=5,
                 node_spacing=30,
                 edge_padding=5):
        self._running = True
        self.screen = None
        self.node1 = None
        self.size = self.width, self.height = width, height

        self.node_r = node_r
        self.node_padding = node_padding
        self.node_spacing = node_spacing

        self.edge_padding = edge_padding

        self.graph = Graph()
        self.pen_mode = 1
        self.prev_pen_mode = 1
        self._node_label_index = 1

        self.search_clear()

        print("1 - Add nodes")
        print("2 - Add undirected edges")
        print("3 - Add directed edges")
        print("4 - Delete nodes")
        print("5 - Delete edges")
        print("6 - Select Start")
        print("7 - Select Goal")
        print("8 - Begin Search")
        print("9 - Clear Search")
        print("Keypad - change search settings")
        print("0 - Clear")
Exemplo n.º 4
0
class App:
    def __init__(self,
                 width=640,
                 height=400,
                 node_r=5,
                 node_padding=5,
                 node_spacing=30,
                 edge_padding=5):
        self._running = True
        self.screen = None
        self.node1 = None
        self.size = self.width, self.height = width, height

        self.node_r = node_r
        self.node_padding = node_padding
        self.node_spacing = node_spacing

        self.edge_padding = edge_padding

        self.graph = Graph()
        self.pen_mode = 1
        self.prev_pen_mode = 1
        self._node_label_index = 1

        self.search_clear()

        print("1 - Add nodes")
        print("2 - Add undirected edges")
        print("3 - Add directed edges")
        print("4 - Delete nodes")
        print("5 - Delete edges")
        print("6 - Select Start")
        print("7 - Select Goal")
        print("8 - Begin Search")
        print("9 - Clear Search")
        print("Keypad - change search settings")
        print("0 - Clear")

    def search_clear(self, target_clear=True):
        if target_clear:
            self.start = None
            self.goal = None
        self.queue = []
        self.search_type = None
        self.enqueue_filter = None
        self.slow_mode = None
        self.expanded_nodes = set()
        self.cur_path = [Path()]
        self.goal_path = Path()
        self.count = 0
        self.w = None
        self.admis_heur = None

        self.searching = False
        self.done = False

    def on_init(self):
        pygame.init()

        self.clock = pygame.time.Clock()

        # Set logo and Title
        logo = pygame.image.load("logo.png")
        pygame.display.set_icon(logo)
        pygame.display.set_caption("Search Algorithms")

        self.screen = pygame.display.set_mode(
            self.size, pygame.HWSURFACE | pygame.DOUBLEBUF)
        self.surface = pygame.Surface(self.screen.get_size())

        self._running = True

    def on_event(self, event):
        if event.type == pygame.QUIT:
            self._running = False

        if (not self.searching) or self.done:
            if event.type == pygame.KEYDOWN:
                if pygame.key.get_pressed()[pygame.K_0]:
                    self.pen_mode = 0

                    self.prev_pen_mode = 1
                    self._node_label_index = 1

                    nodes = self.graph.get_nodes().copy()
                    for node in nodes:
                        self.graph.delete_node(node)

                    self.search_clear()
                elif pygame.key.get_pressed()[pygame.K_1]:
                    self.node1 = None
                    self.pen_mode = 1
                elif pygame.key.get_pressed()[pygame.K_2]:
                    self.pen_mode = 2
                elif pygame.key.get_pressed()[pygame.K_3]:
                    self.pen_mode = 3
                elif pygame.key.get_pressed()[pygame.K_4]:
                    self.pen_mode = 4
                elif pygame.key.get_pressed()[pygame.K_5]:
                    self.pen_mode = 5
                elif pygame.key.get_pressed()[pygame.K_6]:
                    self.pen_mode = 6
                elif pygame.key.get_pressed()[pygame.K_7]:
                    self.pen_mode = 7
                elif pygame.key.get_pressed()[pygame.K_8]:
                    self.pen_mode = 8

                    self.searching = True
                elif pygame.key.get_pressed()[pygame.K_9]:
                    self.pen_mode = 9

                    self.search_clear(False)
                elif pygame.key.get_pressed()[pygame.K_KP0]:
                    self.enqueue_filter = not self.enqueue_filter
                    print("enqueue filter : %s" % (self.enqueue_filter))
                elif pygame.key.get_pressed()[pygame.K_KP1]:
                    self.slow_mode = not self.slow_mode
                    print("slow mode : %s" % (self.slow_mode))
                elif pygame.key.get_pressed()[pygame.K_KP2]:
                    self.admis_heur = not self.admis_heur
                    print("admissable heuristic : %s" % (self.admis_heur))
                elif pygame.key.get_pressed()[pygame.K_KP7]:
                    self.search_type = 'Breadth'
                    print("search type : %s" % (self.search_type))
                elif pygame.key.get_pressed()[pygame.K_KP8]:
                    self.search_type = 'Depth'
                    print("search type : %s" % (self.search_type))
                elif pygame.key.get_pressed()[pygame.K_KP4]:
                    self.search_type = 'Hill'
                    print("search type : %s" % (self.search_type))
                elif pygame.key.get_pressed()[pygame.K_KP5]:
                    self.search_type = 'Beam'
                    print("search type : %s" % (self.search_type))
                elif pygame.key.get_pressed()[pygame.K_KP6]:
                    self.search_type = 'B & B'
                    print("search type : %s" % (self.search_type))
                elif pygame.key.get_pressed()[pygame.K_KP_PLUS]:
                    if self.w == None:
                        self.w = 1
                    else:
                        self.w += 1
                    print("Beam width: %s" % (self.w))
                elif pygame.key.get_pressed()[pygame.K_KP_MINUS]:
                    if self.w == None:
                        self.w = 1
                    else:
                        self.w = max(1, self.w - 1)
                    print("Beam width: %s" % (self.w))

            if self.prev_pen_mode != self.pen_mode:
                self.node1 = None

            if event.type == pygame.MOUSEBUTTONDOWN:
                if self.pen_mode == 1:
                    if self.get_cur_mouse_node(self.node_spacing) == None:
                        self.graph.add_node(
                            Node(self._node_label_index,
                                 pygame.mouse.get_pos()))
                        self._node_label_index += 1

                if self.pen_mode == 2:
                    if self.node1 != None:
                        node2 = self.get_cur_mouse_node(self.node_padding)
                        if node2 != None:
                            pre_existing_edges = set.intersection(
                                self.node1.get_connected_edges(),
                                node2.get_connected_edges())
                            if self.node1 != node2 and len(
                                    pre_existing_edges) == 0:
                                dist = sqr_dist(self.node1, node2)
                                weight = dist**0.5
                                self.graph.add_edge(
                                    UndirectedEdge(self.node1, node2, weight))
                            else:
                                print("No no, not allowed")
                        self.node1 = None

                    else:
                        self.node1 = self.get_cur_mouse_node()

                if self.pen_mode == 3:
                    if self.node1 != None:
                        node2 = self.get_cur_mouse_node(self.node_padding)
                        if node2 != None:
                            pre_existing_edges = set.intersection(
                                self.node1.get_connected_edges(),
                                node2.get_connected_edges())
                            pre_existing_edges = {edge for edge in pre_existing_edges if \
                                                  edge.get_node2() == node2 or edge.__class__ == UndirectedEdge}
                            if self.node1 != node2 and len(
                                    pre_existing_edges) == 0:
                                dist = sqr_dist(self.node1, node2)
                                weight = dist**0.5
                                self.graph.add_edge(
                                    DirectedEdge(self.node1, node2, weight))
                            else:
                                print("No no, not allowed")
                        self.node1 = None

                    else:
                        self.node1 = self.get_cur_mouse_node()

                if self.pen_mode == 4:
                    node = self.get_cur_mouse_node(self.node_padding)
                    if node != None:
                        if node == self.goal:
                            self.goal = None
                        if node == self.start:
                            self.start = None
                        self.graph.delete_node(node)

                if self.pen_mode == 5:
                    edge = self.get_cur_mouse_edge(self.edge_padding)
                    if edge != None:
                        if edge in self.goal_path.get_edges():
                            self.search_clear(False)
                        self.graph.delete_edge(edge)

                if self.pen_mode == 6:
                    node = self.get_cur_mouse_node(self.node_padding)
                    if node and node != self.goal:
                        self.start = node

                if self.pen_mode == 7:
                    node = self.get_cur_mouse_node(self.node_padding)
                    if node and node != self.start:
                        self.goal = node

                if self.pen_mode == 8:
                    print(self.get_cur_mouse_edge(self.edge_padding))

            self.prev_pen_mode = self.pen_mode

    def get_cur_mouse_node(self, padding=0):
        cur_mouse_node = None
        mouse_pos = pygame.mouse.get_pos()
        for node in self.graph.get_nodes():
            node_pos = node.get_coords()
            x_dif = node_pos[0] - mouse_pos[0]
            y_dif = node_pos[1] - mouse_pos[1]
            if x_dif * x_dif + y_dif * y_dif < (self.node_r + padding) * (
                    self.node_r + padding):
                cur_mouse_node = node
                break
        return cur_mouse_node

    def get_cur_mouse_edge(self, padding=0):
        cur_mouse_edge = None
        mouse_pos = pygame.mouse.get_pos()
        for edge in self.graph.get_edges():
            mult = 0
            pre_existing_edges = set.intersection(
                edge.get_node1().get_connected_edges(),
                edge.get_node2().get_connected_edges())
            if len(pre_existing_edges) == 2:
                mult = 1
            node1 = edge.get_node1().get_coords()
            node2 = edge.get_node2().get_coords()
            v0_x = node1[1] - node2[1]
            v0_y = -(node1[0] - node2[0])
            v0_mag = float(v0_x * v0_x + v0_y * v0_y)**0.5
            LHS = (mouse_pos[0] - node1[0])*v0_x\
                + (mouse_pos[1] - node1[1])*v0_y\
                + mult*self.node_r*v0_mag
            RHS = padding * v0_mag
            condition1 = min(node1[0],node2[0]) - self.node_r < mouse_pos[0] and \
                         mouse_pos[0] < max(node1[0],node2[0]) + self.node_r
            condition2 = min(node1[1],node2[1]) - self.node_r < mouse_pos[1] and \
                         mouse_pos[1] < max(node1[1],node2[1]) + self.node_r
            condition = abs(LHS) < abs(RHS) and condition1 and condition2
            if condition:
                cur_mouse_edge = edge
                break
        return cur_mouse_edge

    def on_loop(self):
        if self.searching and not self.done:
            if self.start == None:
                print("Please define a start")
                self.searching = False
                return
            if self.goal == None:
                print("Please define a goal")
                self.searching = False
                return
            if self.search_type == None:
                print("Please select a search type")
                self.searching = False
                return
            if self.enqueue_filter == None:
                print("please toggle enqueue filter")
                self.searching = False
                return
            if self.slow_mode == None:
                print("please toggle slow mode")
                self.searching = False
                return
            if self.search_type == "Beam" and self.w == None:
                print("please define the beam width")
                self.searching = False
                return
            if self.search_type == "B & B" and self.admis_heur == None:
                print("please Toggle admissable heuristic")
                self.searching = False
                return

            if len(self.cur_path[0]) == 0 and len(self.queue) == 0:
                self.queue = [Path([self.start])]
                self.count += 1
                return

            if self.slow_mode == False or (self.slow_mode == True
                                           and self.count == 0):
                if len(self.queue) == 0:
                    self.done = True
                    print("No path found")
                    return

                if self.search_type == 'Beam':
                    self.cur_path = []
                    extensions = []
                    for path in self.queue:
                        if path[-1] == self.goal:
                            self.done = True
                            self.goal_path = path
                            print(path.get_length())
                            return
                        self.cur_path.append(path)
                        extensions += path.extend()
                        if self.enqueue_filter:
                            self.expanded_nodes.add(path[-1])
                    extensions = sorted(
                        extensions,
                        key=lambda path: sqr_dist(path[-1], self.goal))
                    self.queue = []
                else:
                    cur_path = self.queue.pop()
                    if cur_path[-1] == self.goal:
                        self.done = True
                        self.goal_path = cur_path
                        print(cur_path.get_length())
                        return
                    self.cur_path = [cur_path]
                    if self.enqueue_filter:
                        self.expanded_nodes.add(cur_path[-1])
                    extensions = cur_path.extend()
                    if self.search_type == 'Hill':
                        extensions = sorted(
                            extensions,
                            key=lambda path: sqr_dist(path[-1], self.goal),
                            reverse=True)
                print(self.cur_path)
                for path in extensions:
                    if self.enqueue_filter and path[-1] in self.expanded_nodes:
                        continue
                    elif self.search_type == 'Breadth':
                        self.queue.insert(0, path)
                    elif self.search_type == 'Depth':
                        self.queue.append(path)
                    elif self.search_type == 'Hill':
                        self.queue.append(path)
                    elif self.search_type == 'Beam':
                        self.queue.append(path)
                    elif self.search_type == 'B & B':
                        self.queue.append(path)
                if self.search_type == 'Beam':
                    self.queue = self.queue[:self.w]
                if self.search_type == 'B & B':
                    if self.admis_heur:
                        key = lambda path: path.get_length() + sqr_dist(
                            path[-1], self.goal)**0.5
                    else:
                        key = lambda path: path.get_length()
                    self.queue = sorted(self.queue, key=key, reverse=True)
            self.count += 1
            self.count = self.count % 15

    def on_render(self):
        # Draw background
        pygame.draw.rect(self.surface, (255, 255, 255),
                         (0, 0, self.width, self.height))

        # Draw all the edges
        for edge in self.graph.get_edges():
            conditions = [
                True if edge in path.get_edges() else False
                for path in self.cur_path
            ]
            if self.searching and not self.done and True in conditions:
                colour = (255, 100, 100)
                width = 3
            elif self.done and (edge in self.goal_path.get_edges()):
                colour = (0, 255, 0)
                width = 3
            else:
                colour = (0, 0, 0)
                width = 2
            node1_coords = edge.get_node1().get_coords()
            node2_coords = edge.get_node2().get_coords()
            if edge.__class__ == DirectedEdge:
                mult = 0
                pre_existing_edges = set.intersection(
                    edge.get_node1().get_connected_edges(),
                    edge.get_node2().get_connected_edges())
                if len(pre_existing_edges) == 2:
                    mult = 1
                midpoint = ((node1_coords[0] + node2_coords[0]) / 2,
                            (node1_coords[1] + node2_coords[1]) / 2)
                grad_vec = (node2_coords[0] - node1_coords[0],
                            node2_coords[1] - node1_coords[1])
                grad_vec_mag = (grad_vec[0] * grad_vec[0] +
                                grad_vec[1] * grad_vec[1])**(0.5)
                norm_grad_vec = (grad_vec[0] / grad_vec_mag,
                                 grad_vec[1] / grad_vec_mag)
                norm_perp_vec = (norm_grad_vec[1], -norm_grad_vec[0])
                midpoint = (midpoint[0] +
                            mult * self.node_r * norm_perp_vec[0],
                            midpoint[1] +
                            mult * self.node_r * norm_perp_vec[1])
                head_1_grad = (10 * (0.8660254037844386 * norm_grad_vec[0] -
                                     0.5 * norm_grad_vec[1]),
                               10 * (0.5 * norm_grad_vec[0] +
                                     0.8660254037844386 * norm_grad_vec[1]))
                head_2_grad = (10 * (0.8660254037844386 * norm_grad_vec[0] +
                                     0.5 * norm_grad_vec[1]),
                               10 * (-0.5 * norm_grad_vec[0] +
                                     0.8660254037844386 * norm_grad_vec[1]))
                pygame.draw.line(self.surface, colour, midpoint,
                                 (midpoint[0] - head_1_grad[0],
                                  midpoint[1] - head_1_grad[1]),
                                 max(width / 2, 1))
                pygame.draw.line(self.surface, colour, midpoint,
                                 (midpoint[0] - head_2_grad[0],
                                  midpoint[1] - head_2_grad[1]),
                                 max(width / 2, 1))
                node1_coords = (node1_coords[0] +
                                mult * self.node_r * norm_perp_vec[0],
                                node1_coords[1] +
                                mult * self.node_r * norm_perp_vec[1])
                node2_coords = (node2_coords[0] +
                                mult * self.node_r * norm_perp_vec[0],
                                node2_coords[1] +
                                mult * self.node_r * norm_perp_vec[1])
            pygame.draw.line(self.surface, colour, node1_coords, node2_coords,
                             width)

        # Draw a line between node and mouse when drawing edges
        if self.node1 != None:
            pygame.draw.line(self.surface, (0, 0, 0), self.node1.get_coords(),
                             pygame.mouse.get_pos())

        # Draw nodes
        for node in self.graph.get_nodes():
            if node == self.start:
                colour = (0, 255, 0)
            elif node == self.goal:
                colour = (255, 0, 0)
            elif node in self.expanded_nodes:
                colour = (0, 0, 255)
            else:
                colour = (0, 0, 0)
            pygame.draw.circle(self.surface, colour, node.get_coords(),
                               self.node_r)

        self.screen.blit(self.surface, (0, 0))

        pygame.display.flip()

    def on_cleanup(self):
        pygame.quit()

    def on_execute(self):
        if self.on_init() == False:
            self._running = False

        while (self._running):
            for event in pygame.event.get():
                self.on_event(event)
            self.on_loop()
            self.on_render()
            self.clock.tick(60)
        self.on_cleanup()
Exemplo n.º 5
0
    print("Result length of the path: ", length)

    return length, path


def sort_closest_bars(adjacency_matrix):
    result = {}

    for key, value in adjacency_matrix.items():
        result[key] = {
            k: v
            for k, v in sorted(value.items(), key=lambda item: item[1])
        }

    return result


def find_next_bar(current, adjacency_matrix, visited):
    for key, value in adjacency_matrix[current].items():
        next_bar = key
        if next_bar not in visited:
            return next_bar
    return 0


if __name__ == '__main__':
    graph = graph.Graph()
    graph.build_graph()

    christofides(graph, 0, 5)
Exemplo n.º 6
0
class AppWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(AppWidget, self).__init__(parent)
        self.graph = Graph()
        self.mode = '+ .'
        self._next_label = 1

        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.searchLoop)

        self.nodeR = 5
        self.nodeDetectPadding = 7
        self.nodeSpacing = 30

        self.edgeDetectPadding = 3

        self.clearSearchVars()

        self.setupUi()

    def setupUi(self):
        self.setMouseTracking(True)

        pal = self.palette()
        pal.setColor(QtGui.QPalette.Background, QtCore.Qt.white)
        self.setAutoFillBackground(True)
        self.setPalette(pal)

    def clearSearchVars(self, clear_target=True):
        if clear_target:
            self.start = None
            self.goal = None

        self.node1 = None

        self.queue = []
        self.searchAlgo = None
        self.listFilter = None
        self.speed = 0
        self.expandedNodes = set()
        self.curPaths = [Path()]
        self.goalPath = Path()
        self.w = 1
        self.admisHeur = None

        self.searching = False
        self.done = False

        self.changeSpeed()

    def changeSpeed(self):
        self.timer.setInterval(self.speed)

    def startTimer(self):
        if self.searchAlgo != None:
            self.timer.start(self.speed)

    def searchLoop(self):
        if self.done:
            self.searching = False
            self.timer.stop()
            self.repaint()
            return

        if len(self.curPaths[0]) == 0 and len(self.queue) == 0:
            self.searching = True
            self.queue = [Path([self.start])]
            return

        if len(self.queue) == 0:
            self.done = True
            print("No path found")
            return

        if self.searchAlgo == 'Beam':
            self.curPaths = []
            extensions = []
            for path in self.queue:
                if path[-1] == self.goal:
                    self.done = True
                    self.goalPath = path
                    print(path.get_length())
                    return
                self.curPaths.append(path)
                extensions += path.extend()
                if self.listFilter:
                    self.expandedNodes.add(path[-1])
            extensions = sorted(extensions,
                                key=lambda path: sqr_dist(path[-1], self.goal))
            self.queue = []
        else:
            cur_path = self.queue.pop()
            if cur_path[-1] == self.goal:
                self.done = True
                self.goalPath = cur_path
                print(cur_path.get_length())
                return
            self.curPaths = [cur_path]
            if self.listFilter:
                self.expandedNodes.add(cur_path[-1])
            extensions = cur_path.extend()
            if self.searchAlgo == 'Hill':
                extensions = sorted(
                    extensions,
                    key=lambda path: sqr_dist(path[-1], self.goal),
                    reverse=True)
        print(self.curPaths)
        for path in extensions:
            if self.listFilter and path[-1] in self.expandedNodes:
                continue
            elif self.searchAlgo == 'Breadth':
                self.queue.insert(0, path)
            elif self.searchAlgo == 'Depth':
                self.queue.append(path)
            elif self.searchAlgo == 'Hill':
                self.queue.append(path)
            elif self.searchAlgo == 'Beam':
                self.queue.append(path)
            elif self.searchAlgo == 'BnB':
                self.queue.append(path)
        if self.searchAlgo == 'Beam':
            self.queue = self.queue[:self.w]
        if self.searchAlgo == 'BnB':
            if self.admisHeur:
                key = lambda path: path.get_length() + sqr_dist(
                    path[-1], self.goal)**0.5
            else:
                key = lambda path: path.get_length()
            self.queue = sorted(self.queue, key=key, reverse=True)
        self.repaint()

    def paintEvent(self, event):
        painter = QtGui.QPainter()
        painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
        painter.begin(self)
        self.draw_edges(painter)
        if self.node1 != None and self.mouse_pos != None:
            self.draw_new_edge(painter)
        self.draw_nodes(painter)
        painter.end()

    def draw_nodes(self, painter):
        pen = QtGui.QPen(QtCore.Qt.black, 1, QtCore.Qt.SolidLine)
        brush = QtGui.QBrush(QtCore.Qt.black, QtCore.Qt.SolidPattern)
        nodes = self.graph.get_nodes()
        for node in nodes:
            if self.start == node:
                pen.setColor(QtCore.Qt.green)
                brush.setColor(QtCore.Qt.green)
            elif self.goal == node:
                pen.setColor(QtCore.Qt.red)
                brush.setColor(QtCore.Qt.red)
            elif node in self.expandedNodes:
                pen.setColor(QtCore.Qt.blue)
                brush.setColor(QtCore.Qt.blue)
            else:
                pen.setColor(QtCore.Qt.black)
                brush.setColor(QtCore.Qt.black)
            painter.setPen(pen)
            painter.setBrush(brush)
            pos = node.get_coords()
            painter.drawEllipse(pos[0] - self.nodeR, pos[1] - self.nodeR,
                                2 * self.nodeR, 2 * self.nodeR)

    def draw_edges(self, painter):
        pen = QtGui.QPen(QtCore.Qt.black, 3, QtCore.Qt.SolidLine)
        brush = QtGui.QBrush(QtCore.Qt.black, QtCore.Qt.SolidPattern)
        edges = self.graph.get_edges()
        for edge in edges:
            conditions = [
                True if edge in path.get_edges() else False
                for path in self.curPaths
            ]
            if self.searching and not self.done and True in conditions:
                pen.setColor(QtCore.Qt.red)
                pen.setWidth(3)
            elif self.done and (edge in self.goalPath.get_edges()):
                pen.setColor(QtCore.Qt.green)
                pen.setWidth(3)
            else:
                pen.setColor(QtCore.Qt.black)
                pen.setWidth(2)
            painter.setPen(pen)
            painter.setBrush(brush)
            node1 = edge.get_node1()
            node2 = edge.get_node2()
            pos1 = node1.get_coords()
            pos2 = node2.get_coords()
            if edge.__class__ == DirectedEdge:
                mult = 0
                pre_existing_edges = set.intersection(
                    edge.get_node1().get_connected_edges(),
                    edge.get_node2().get_connected_edges())
                if len(pre_existing_edges) == 2:
                    mult = 1
                midpoint = ((pos1[0] + pos2[0]) / 2, (pos1[1] + pos2[1]) / 2)
                grad_vec = (pos2[0] - pos1[0], pos2[1] - pos1[1])
                grad_vec_mag = (grad_vec[0] * grad_vec[0] +
                                grad_vec[1] * grad_vec[1])**(0.5)
                norm_grad_vec = (grad_vec[0] / grad_vec_mag,
                                 grad_vec[1] / grad_vec_mag)
                norm_perp_vec = (norm_grad_vec[1], -norm_grad_vec[0])
                midpoint = (midpoint[0] + mult * self.nodeR * norm_perp_vec[0],
                            midpoint[1] + mult * self.nodeR * norm_perp_vec[1])
                pen.setWidth(1)
                painter.setPen(pen)
                head_1_grad = (10 * (0.8660254037844386 * norm_grad_vec[0] -
                                     0.5 * norm_grad_vec[1]),
                               10 * (0.5 * norm_grad_vec[0] +
                                     0.8660254037844386 * norm_grad_vec[1]))
                head_2_grad = (10 * (0.8660254037844386 * norm_grad_vec[0] +
                                     0.5 * norm_grad_vec[1]),
                               10 * (-0.5 * norm_grad_vec[0] +
                                     0.8660254037844386 * norm_grad_vec[1]))
                pen.setWidth(3)
                painter.setPen(pen)
                painter.drawLine(midpoint[0], midpoint[1],
                                 midpoint[0] - head_1_grad[0],
                                 midpoint[1] - head_1_grad[1])
                painter.drawLine(midpoint[0], midpoint[1],
                                 midpoint[0] - head_2_grad[0],
                                 midpoint[1] - head_2_grad[1])
                pos1 = (pos1[0] + mult * self.nodeR * norm_perp_vec[0],
                        pos1[1] + mult * self.nodeR * norm_perp_vec[1])
                pos2 = (pos2[0] + mult * self.nodeR * norm_perp_vec[0],
                        pos2[1] + mult * self.nodeR * norm_perp_vec[1])
            painter.drawLine(pos1[0], pos1[1], pos2[0], pos2[1])

    def draw_new_edge(self, painter):
        pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine)
        brush = QtGui.QBrush(QtCore.Qt.black, QtCore.Qt.SolidPattern)
        painter.setPen(pen)
        painter.setBrush(brush)
        node1 = self.node1
        pos1 = node1.get_coords()
        pos2 = self.mouse_pos
        painter.drawLine(pos1[0], pos1[1], pos2[0], pos2[1])
        self.mouse_pos = None

    def mouseMoveEvent(self, event):
        if self.node1 != None:
            pos = event.pos()
            self.mouse_pos = (pos.x(), pos.y())
            self.repaint()

    def mousePressEvent(self, event):
        if not self.searching:
            if self.mode == '+ .':
                pos = event.pos()
                pos = (pos.x(), pos.y())
                if self.getNodeAtPos(pos, self.nodeSpacing) == None:
                    node = Node(self._next_label, pos)
                    self.graph.add_node(node)
                    self._next_label += 1
                    self.repaint()
                else:
                    # TODO send message to status bar saying not allowed
                    pass
            if self.mode == '+ ---':
                pos = event.pos()
                pos = (pos.x(), pos.y())
                self.mouse_pos = pos
                node = self.getNodeAtPos(pos, padding=self.nodeDetectPadding)
                if node == None:
                    self.node1 = None
                elif self.node1 == None:
                    self.node1 = node
                elif self.node1 != node:
                    node1 = self.node1
                    node2 = node
                    pre_existing_edges = set.intersection(
                        self.node1.get_connected_edges(),
                        node2.get_connected_edges())
                    if len(pre_existing_edges) == 0:
                        weight = sqr_dist(node1, node2)**0.5
                        self.graph.add_edge(
                            UndirectedEdge(node1, node2, weight))
                    else:
                        # TODO send message to status bar saying not allowed
                        pass
                    self.node1 = None
                else:
                    # TODO send message to status bar saying not allowed
                    pass
                self.repaint()
            if self.mode == '+ -->':
                pos = event.pos()
                pos = (pos.x(), pos.y())
                self.mouse_pos = pos
                node = self.getNodeAtPos(pos, padding=self.nodeDetectPadding)
                if node == None:
                    self.node1 = None
                elif self.node1 == None:
                    self.node1 = node
                elif self.node1 != node:
                    node1 = self.node1
                    node2 = node
                    pre_existing_edges = set.intersection(
                        self.node1.get_connected_edges(),
                        node2.get_connected_edges())
                    pre_existing_edges = {edge for edge in pre_existing_edges if \
                                          edge.get_node2() == node2 or edge.__class__ == UndirectedEdge}
                    if len(pre_existing_edges) == 0:
                        weight = sqr_dist(node1, node2)**0.5
                        self.graph.add_edge(DirectedEdge(node1, node2, weight))
                    else:
                        # TODO send message to status bar saying not allowed
                        pass
                    self.node1 = None
                else:
                    # TODO send message to status bar saying not allowed
                    pass
                self.repaint()
            if self.mode == '- .':
                pos = event.pos()
                pos = (pos.x(), pos.y())
                node = self.getNodeAtPos(pos, self.nodeDetectPadding)
                if node != None:
                    if node == self.start:
                        self.start = None
                    if node == self.goal:
                        self.goal = None
                    if node in self.goalPath:
                        self.clearSearchVars()
                    self.graph.delete_node(node)
                    self.repaint()
            if self.mode == '- ---':
                pos = event.pos()
                pos = (pos.x(), pos.y())
                edge = self.getEdgeAtPos(pos, self.edgeDetectPadding)
                print(edge)
                if edge != None:
                    if edge in self.goalPath.get_edges():
                        self.clearSearchVars(False)
                    self.graph.delete_edge(edge)
                    self.repaint()
            if self.mode == '+ S':
                pos = event.pos()
                pos = (pos.x(), pos.y())
                node = self.getNodeAtPos(pos, self.nodeDetectPadding)
                if node != self.goal:
                    self.start = node
                self.repaint()
            if self.mode == '+ G':
                pos = event.pos()
                pos = (pos.x(), pos.y())
                node = self.getNodeAtPos(pos, self.nodeDetectPadding)
                if node != self.start:
                    self.goal = node
                self.repaint()

    def getNodeAtPos(self, pos, padding=0):
        cur_mouse_node = None
        for node in self.graph.get_nodes():
            node_pos = node.get_coords()
            x_dif = node_pos[0] - pos[0]
            y_dif = node_pos[1] - pos[1]
            if x_dif * x_dif + y_dif * y_dif < (self.nodeR + padding) * (
                    self.nodeR + padding):
                cur_mouse_node = node
                break
        return cur_mouse_node

    def getEdgeAtPos(self, pos, padding=0):
        cur_mouse_edge = None
        for edge in self.graph.get_edges():
            pos1 = edge.get_node1().get_coords()
            pos2 = edge.get_node2().get_coords()
            m = (pos1[0] - pos2[0]) * (pos1[0] - pos2[0]) + (
                pos1[1] - pos2[1]) * (pos1[1] - pos2[1])
            px = (pos[0] - pos1[0]) * (pos2[0] - pos1[0]) + (
                pos[1] - pos1[1]) * (pos2[1] - pos1[1])
            py = (pos[1] - pos1[1]) * (pos2[0] - pos1[0]) - (
                pos[0] - pos1[0]) * (pos2[1] - pos1[1])
            p2 = (pos[0] - pos2[0]) * (pos[0] - pos2[0]) + (
                pos[1] - pos2[1]) * (pos[1] - pos2[1])
            condition1 = 0 < px and px < m and py * py < padding * padding * m
            condition2 = px < 0 and px * px + py * py < padding * padding
            condition3 = px > m and p2 < padding * padding
            if condition1 or condition2 or condition3:
                cur_mouse_edge = edge
                break
        return cur_mouse_edge