diff --git a/resources/graphs/basic_graph/components.py b/resources/graphs/basic_graph/components.py index 3fff6e5e01d43eb155d7229470cb6dbb70ebf79b..9a44bcf38cd62afa72152391f459563331a7de8d 100644 --- a/resources/graphs/basic_graph/components.py +++ b/resources/graphs/basic_graph/components.py @@ -229,6 +229,8 @@ class BasicNode(): self._edges = {} self._connected_nodes = {} self._graph = None + self.x_coord = None + self.y_coord = None # Define expected class types (should all be of "Basic" type). # This is necessary for inheritance, or else child classes will only have access to parent functions. diff --git a/resources/graphs/basic_graph/graph.py b/resources/graphs/basic_graph/graph.py index 70dd6fc3b5e6735e9ddb78a64af7e7acf81835c5..f0a94a16f72e13114e5a33c401dff10e7f2d8f90 100644 --- a/resources/graphs/basic_graph/graph.py +++ b/resources/graphs/basic_graph/graph.py @@ -203,6 +203,7 @@ class BasicGraph(): def _assign_node_coordinates(self): """ Assigns Graph mapping coordinates for all nodes. + Accomplishes this with random assignment, on a grid of 100 by 100, such that no nodes should overlap. """ max_direction = 100 possible_positions = [] @@ -260,7 +261,7 @@ class BasicGraph(): node_1 = node_list['tail_node'] node_2 = node_list['head_node'] - # Assign edge cordinates. + # Assign edge coordinates. self._edges[edge.get_name()].x1_coord = node_1.x_coord self._edges[edge.get_name()].x2_coord = node_2.x_coord self._edges[edge.get_name()].y1_coord = node_1.y_coord diff --git a/resources/graphs/state_machine/graph.py b/resources/graphs/state_machine/graph.py index dc2d2d06f28c11402bd409c75f3b29c7747252e5..ea59992d2cebea5b956497bd05fdd8397bcbcabe 100644 --- a/resources/graphs/state_machine/graph.py +++ b/resources/graphs/state_machine/graph.py @@ -10,6 +10,7 @@ Parent structure containing various Nodes and Edges. """ # System Imports. +from random import randint # User Class Imports. from .components import StateMachineEdge, StateMachineNode @@ -50,6 +51,132 @@ class StateMachineGraph(DirectedGraph): self._graph_type = StateMachineGraph self._node_type = StateMachineNode + def _assign_node_coordinates(self): + """ + Assigns Graph mapping coordinates for all nodes. + Still mostly random coordinate assignment (just like the basic graph). However, slightly more organized. + Accomplishes this by putting initial/start nodes on the far left, and final nodes on far right. + All other node coordinates are randomly assigned to the center. + """ + max_direction = 100 + possible_positions = [] + + # Create list of "possible_positions". Should be a list of every possible (x,y) coordinate pairing. + for x_index in range(int(max_direction)): + # We want to limit x-coords, preventing random assignment to x-coords less than 10 or greater than 90. + if x_index > 10 and x_index < 90: + # Given x-coord is between values we want. Proceed. + for y_index in range(int(max_direction)): + possible_positions.append((x_index, y_index)) + + # Values to remember extreme coord values, as well as associated nodes. + farthest_x = (0, None) + farthest_y = (0, None) + shortest_x = (100, None) + shortest_y = (100, None) + + # Loop through all initial states, assigning y-coords based on a divided grid setup. + # Grids are from top to bottom, to evenly distribute space to all start nodes. + logger.info('Initial states: {0}'.format(self._initial_states)) + initial_state_count = len(self._initial_states) + state_index = 1 + grid_length = max_direction / initial_state_count + for initial_state in self._initial_states.values(): + # Get grid values. + current_grid_end = grid_length * state_index + current_grid_start = current_grid_end - grid_length + state_index += 1 + + # Assign node to calculated grid. + x_coord = 5 + y_coord = int((current_grid_start + current_grid_end) / 2) + self._nodes[initial_state.get_name()].x_coord = x_coord + self._nodes[initial_state.get_name()].y_coord = y_coord + + # Record extreme positions. + if x_coord > farthest_x[0]: + farthest_x = (x_coord, initial_state) + if x_coord < shortest_x[0]: + shortest_x = (x_coord, initial_state) + if y_coord > farthest_y[0]: + farthest_y = (y_coord, initial_state) + if y_coord < shortest_y[0]: + shortest_y = (y_coord, initial_state) + + # Loop through all final states, assigning y-coords based on a divided grid setup. + # Grids are from top to bottom, to evenly distribute space to all final nodes. + final_state_count = len(self._final_states) + state_index = 1 + grid_length = max_direction / final_state_count + for final_state in self._final_states.values(): + # Get grid values. + current_grid_end = grid_length * state_index + current_grid_start = current_grid_end - grid_length + state_index += 1 + + # Assign node to calculated grid. + x_coord = 95 + y_coord = int((current_grid_start + current_grid_end) / 2) + self._nodes[final_state.get_name()].x_coord = x_coord + self._nodes[final_state.get_name()].y_coord = y_coord + + # Record extreme positions. + if x_coord > farthest_x[0]: + farthest_x = (x_coord, final_state) + if x_coord < shortest_x[0]: + shortest_x = (x_coord, final_state) + if y_coord > farthest_y[0]: + farthest_y = (y_coord, final_state) + if y_coord < shortest_y[0]: + shortest_y = (y_coord, final_state) + + # Now loop through all other nodes and assign coordinates. + for node in self.get_all_nodes().values(): + # Make sure node is not already assigned coordinates. + if node.x_coord is None or node.y_coord is None: + # Get a random position. + index = randint(0, len(possible_positions)) + position = possible_positions.pop(index) + self._nodes[node.get_name()].x_coord = position[0] + self._nodes[node.get_name()].y_coord = position[1] + + # Record extreme positions. + if position[0] > farthest_x[0]: + farthest_x = (position[0], node) + if position[0] < shortest_x[0]: + shortest_x = (position[0], node) + if position[1] > farthest_y[0]: + farthest_y = (position[1], node) + if position[1] < shortest_y[0]: + shortest_y = (position[1], node) + + logger.info('Node: {0}'.format(node)) + logger.info('Node x: {0}'.format(node.x_coord)) + logger.info('Node y: {0}'.format(node.y_coord)) + + # Now that all nodes are mapped, adjust extreme node coords, so that all graphs will be roughly the same size. + if shortest_x[0] > 10: + shortest_x[1].x_coord = 10 + if shortest_y[0] > 10: + shortest_y[1].y_coord = 10 + if farthest_x[0] < 90: + farthest_x[1].x_coord = 90 + if farthest_y[0] < 90: + farthest_y[1].y_coord = 90 + + # Now go through all edges and do similar. + for edge in self.get_all_edges().values(): + node_list = edge.get_nodes() + + node_1 = node_list['tail_node'] + node_2 = node_list['head_node'] + + # Assign edge coordinates. + self._edges[edge.get_name()].x1_coord = node_1.x_coord + self._edges[edge.get_name()].x2_coord = node_2.x_coord + self._edges[edge.get_name()].y1_coord = node_1.y_coord + self._edges[edge.get_name()].y2_coord = node_2.y_coord + def _create_node_hover_text(self): """ Hook for child classes to edit node hover text, if needed.