def test_path_intersect_no_mirror_2(self): anchor_points_0 = [ GraphicsPoint(100, 100), Point(473.7132417184837, 208.4978496002687), GraphicsPoint(457, 125) ] anchor_points_1 = [ GraphicsPoint(500, 400), Point(573, 371), Point(550, 177), Point(586, 146), Point(337, 129), Point(536, 22), Point(689, 67), Point(728, 344), Point(650, 472) ] p_start = GraphicsPoint(100, 100) p_end = GraphicsPoint(457, 125) path = Path([ GraphicsBezier(p_start, p_end, Point(278.5, 112.5), Point(404.8987230708711, 188.92401096739675)) ], p_start, p_end) path2 = Path.from_points(anchor_points_1) intersections = path.intersects(path2) intersections_2 = path2.intersects(path) show_paths_2([path, path2], intersections + intersections_2) self.assertEqual(len(intersections), len(intersections_2))
def test_path_intersect_no_mirror_3(self): anchor_points_0 = [ Point(600, 300), Point(763, 232), Point(760, 248), Point(763.3812601455536, 279.9618353762123), Point(759.093503294539, 324.7570933974201), Point(752.2129530873079, 369.22796058983914), Point(728.8190694506658, 407.66916515467096) ] anchor_points_1 = [ Point(100, 100), Point(638, 170), Point(727, 212), Point(594, 357), Point(476, 177), Point(261, 207), Point(167.76045105799363, 197.0495733490862) ] path = Path.from_points(anchor_points_0) path2 = Path.from_points(anchor_points_1) intersections = path.intersects(path2) intersections_2 = path2.intersects(path) show_paths_2([path, path2], intersections + intersections_2) self.assertEqual(len(intersections), len(intersections_2))
def validate_path(self, path: Path) -> Tuple[List[Point], bool]: valid_path = True all_intersections = [] # if preview_path.start_point == preview_path.end_point and (len(preview_path.start_point.paths) + 2 >= 3): if path.start_point.equals(path.end_point): if path.start_point.num_paths + 2 > 3: valid_path = False print("too many connections") # Does created path collide with existing paths, and if so, where for p in self.paths: intersections = path.intersects(p) if intersections: all_intersections.extend(intersections) valid_path = False print("collides with existing paths") self_intersections = path.self_intersections() if self_intersections: all_intersections.extend(self_intersections) valid_path = False print("collides with itself") # If the path is still valid, test if it intersects with any points if valid_path: for point in self.points: if path.point_touches_path(point): all_intersections.append(point) valid_path = False print("collides existing points") return all_intersections, valid_path
def init_dead_ends_0(): positions = {0: (0, 0), 1: (30, 0), 2: (30, 20), 3: (60, 0)} points = init_points(positions) paths = [ Path.from_points([points[0], points[1]]), Path.from_points([points[0], Point(30, -10), points[3]]), Path.from_points([points[1], points[2]]), Path.from_points([points[1], points[3]]) ] return points, init_edge_map(paths)
def find_path_segment(path: Path, point: Point) -> Segment: approximation = path.approximate() if path.start_point == point: return Segment(point, approximation[1]) else: return Segment(point, approximation[-2])
def test_path_intersect_5(self): anchor_points_0 = [ GraphicsPoint(403.01382054213786, 117.09675232181752), GraphicsPoint(450, 100) ] anchor_points_1 = [ GraphicsPoint(422.05639822420903, 417.833171793951), GraphicsPoint(450.55461679819734, 407.4265792951324), GraphicsPoint(500, 400) ] path = Path.from_points(anchor_points_0) path2 = Path.from_points(anchor_points_1) intersections = path.intersects(path2) show_paths_2([path, path2], intersections) self.assertEqual(False, len(intersections) > 0)
def test_path_intersect_4(self): anchor_points_0 = [ GraphicsPoint(440.3994196807172, 149.06963274300102), GraphicsPoint(450, 100) ] anchor_points_1 = [ GraphicsPoint(450, 100), GraphicsPoint(402.0865680692103, 419.70860894843224), GraphicsPoint(422.05639822420903, 417.833171793951) ] path = Path.from_points(anchor_points_0) path2 = Path.from_points(anchor_points_1) intersections = path.intersects(path2) show_paths_2([path, path2], intersections) self.assertEqual(False, len(intersections) > 0)
def build_sub_path(self, node, alt_start_node=None): path_points = [] if alt_start_node: path_points.append(alt_start_node.point) i = 0 while node and i < 4: path_points.append(node.point) node = node.parent i += 1 return Path.from_points_non_graphic(path_points)
def test_path_intersect_3(self): anchor_points_0 = [ GraphicsPoint(100, 100), Point(376, 61), Point(639, 217), GraphicsPoint(546, 357) ] anchor_points_1 = [ GraphicsPoint(450, 100), GraphicsPoint(440.3994196807172, 149.06963274300102) ] path = Path.from_points(anchor_points_0) path2 = Path.from_points(anchor_points_1) intersections = path2.intersects(path) show_paths_2([path, path2], intersections) self.assertEqual(False, len(intersections) > 0)
def test_path_intersect_no_mirror(self): anchor_points_0 = [ GraphicsPoint(450, 100), Point(473.7132417184837, 208.4978496002687), GraphicsPoint(466, 268) ] anchor_points_1 = [ GraphicsPoint(100, 100), Point(230, 177), Point(548, 178), GraphicsPoint(701, 347) ] path = Path.from_points(anchor_points_0) path2 = Path.from_points(anchor_points_1) intersections = path.intersects(path2) intersections_2 = path2.intersects(path) show_paths_2([path, path2], intersections + intersections_2) self.assertEqual(len(intersections), len(intersections_2))
def test_path_self_no_intersect(self): start_end = GraphicsPoint(376, 95) anchor_points = [ start_end, Point(154, 199), Point(302, 315), Point(493, 120), start_end ] path = Path.from_points(anchor_points) intersections = path.self_intersections() show_paths_2([path], intersections) self.assertEqual(0, len(intersections))
def init_connected_faces(): positions = {0: (0, 0), 1: (30, 0), 2: (50, 0), 3: (70, 0), 4: (90, 0)} points = init_points(positions) paths = [ Path.from_points([points[0], points[1]]), Path.from_points([points[0], Point(15, -10), points[1]]), Path.from_points([points[1], points[2]]), Path.from_points([points[2], points[3]]), Path.from_points([points[3], points[4]]), Path.from_points([points[3], Point(15, -10), points[4]]) ] return points, init_edge_map(paths)
def init_onion_graph(): positions = { 0: (0, 0), 1: (0, 30), 2: (0, -20), 3: (0, 10), 4: (0, 20), 5: (0, -10) } points = init_points(positions) paths = [ Path.from_points([points[0], Point(-10, 5), points[3]]), Path.from_points([points[0], Point(10, 5), points[3]]), Path.from_points([points[0], points[5]]), Path.from_points([points[5], points[2]]), Path.from_points([points[1], Point(-30, 0), points[2]]), Path.from_points([points[1], Point(30, 0), points[2]]), Path.from_points([points[1], points[4]]), Path.from_points([points[3], points[4]]), ] return points, init_edge_map(paths)
def run_game(self, initial_points, initial_paths): self.set_initial_state(initial_points, initial_paths) last_mouse_pos = (0, 0) spacing = " " legend_text = "Esc: cancel path" + spacing + "Right Click: Path suggestion" + spacing + "Left Click: Draw path" + spacing + "Backspace: Main menu" legend = Label(0, 0, 800, 30, pygame.SRCALPHA, pygame.SRCALPHA, 0, legend_text, 18, gc.UI_BUTTON_TEXT_COLOR) player_1_label = Label(0, 0, 60, 30, gc.PLAYER_1_COLOR, gc.BLACK, 2, "Player 1", 20, gc.UI_BUTTON_TEXT_COLOR) player_2_label = Label(gc.WINDOW_WIDTH - 60, 0, 60, 30, gc.PLAYER_2_COLOR, gc.PLAYER_2_COLOR, 2, "Player 2", 20, gc.UI_BUTTON_TEXT_COLOR) surfaces = [] surfaces.extend( [legend.draw(), player_1_label.draw(), player_2_label.draw()]) if self.game_over(self.base_region): self.update_screen([]) return (State.GAME_OVER, [initial_points, initial_paths]) while True: current_mouse_pos = pygame.mouse.get_pos() surfaces.extend( [legend.draw(), player_1_label.draw(), player_2_label.draw()]) for event in pygame.event.get(): # exit via X in window corner if event.type == pygame.QUIT: sys.exit() # key pressed if event.type == pygame.KEYDOWN: if event.key == pygame.K_BACKSPACE: # self.SCREEN.fill(gc.BACKGROUND_COLOR) self.hard_reset() return (State.MAIN_MENU, []) self.key_event_handler(event) # mouse clicked if event.type == pygame.MOUSEBUTTONDOWN: mouse_point = Point(*event.pos) current_region = self.base_region.find_region( Point(*event.pos)) # p is a Point if collision exists, else None p = self.point_collision(current_region.game_points, event.pos) self.update_screen(surfaces) # right click = suggest a path (if point pressed is GraphicsPoint if event.button == 3 and p: self.suggest_points.append(p) if len(self.suggest_points) == 2: suggested_path_points = self.find_path() # User canceled if not suggested_path_points: self.soft_reset() break self.finalize_path(suggested_path_points) self.change_turn(player_1_label, player_2_label) # check for game over state if self.game_over(self.base_region): self.update_screen([]) return (State.GAME_OVER, [initial_points, initial_paths]) # reset control variables self.soft_reset() break elif event.button == 3: break # Cant draw straight lines by pressing twice in the same pixel if len(self.preview_points) > 0 and mouse_point.equals( self.preview_points[-1]): break # point collision and not drawing a path already if p and not self.creating_path: self.preview_path = None self.all_intersections = [] self.creating_path = True self.preview_points = [p] self.preview_path = Path.from_points( self.preview_points + [Point(*event.pos)]) # no point collision and creating path already elif not p and self.creating_path: self.preview_points.append(Point(*event.pos)) # point collision and creating path already elif p and self.creating_path: if p == self.preview_points[0] and len( self.preview_points) == 1: self.soft_reset() break self.preview_points.append(p) self.preview_path = Path.from_points( self.preview_points) (self.all_intersections, valid_path) = self.validate_path(self.preview_path) # path is not valid, show intersections if not valid_path: self.creating_path = False self.preview_path.change_color(gc.RED) self.all_intersections = self.points_to_graphicspoints( self.all_intersections, gc.BLACK, 5) self.soft_reset() break # Split path in two and calculate new point self.finalize_path(self.preview_points) self.change_turn(player_1_label, player_2_label) if self.game_over(self.base_region): self.update_screen([]) return (State.GAME_OVER, [initial_points, initial_paths]) self.soft_reset() if self.preview_points and self.creating_path: cur_point = Point(*pygame.mouse.get_pos()) self.preview_path.redraw(self.preview_points + [cur_point], self.get_color()) # Update the screen if the cursor has moved if last_mouse_pos != current_mouse_pos: self.SCREEN.fill(gc.BACKGROUND_COLOR) last_mouse_pos = current_mouse_pos self.update_screen(surfaces) pygame.display.update() surfaces = [] # controls fps self.CLOCK.tick(gc.FPS)
def build_complete_path(self, node): path_points = [] while node: path_points.append(node.point) node = node.parent return path_points, Path.from_points(path_points)
def insert_rotation(self, new_region: 'Region'): # Find all exclusions in self, that need to be moved to new region rotation_regions = set() new_region_splitted = False clear_region = False for exclusion in self.exclusions: all_points_in_region = True # All game point of current exclusion are located within the new region all_on_border = True # All of the current exclusion's game point are located on the border of the new region border_set = new_region.get_outer_set() # All game points on the border of the new region on_border = 0 # amount of the exclusion's game points that are located on the border of the new region #Get the game point of the current exclusion if exclusion.game_points: points_inside_exclusion = exclusion.game_points else: points_inside_exclusion = exclusion.get_outer_set() #Iterate the current exclusion's game points for game_point in points_inside_exclusion: if game_point in border_set: # Is the point part of the new regions border? on_border += 1 if not (new_region.is_point_in_polygon(game_point) or game_point in new_region.game_points): #Is the point inside the new region? all_points_in_region = False break # The exclusion lies inside new_region (sharply) if all_points_in_region and on_border < 1: rotation_regions.add(exclusion) elif all_points_in_region and on_border > 1: # The exclusion either split the new_region in two # Or is a neighbouring region exclusion_cycle = set(exclusion.cycle) all_paths_in_region = True # Validate whether all paths in exclusion are contained within new_region for path in exclusion_cycle: if not path in new_region.cycle: # Find if path is inside the region path_mid_point = Path.calculate_mid_point(path) if not new_region.is_point_in_region(path_mid_point): all_paths_in_region = False break # The exclusion is contained within new_region (and new_region is split in two) if all_paths_in_region: if new_region_splitted: clear_region = True else: new_region_splitted = True rotation_regions.add(exclusion) # Update the parent regions exclusions parent_regions = self.exclusions.difference(rotation_regions) parent_regions.add(new_region) self.exclusions = parent_regions #Add exclusions to the new region new_region.exclusions = rotation_regions #Update the parent of the new region's exclusions for region in rotation_regions: region.parent = new_region #Update the new region's edge map and the parent region's (self) new_region.add_edges(rotation_regions) self.remove_edges(rotation_regions) # Is the new region a container region? If so, clear it if clear_region: new_region.clear() return new_region_splitted
def faces(self, root: Point, came_from: Point, path_to_follow: Path, directions_taken): next_point = path_to_follow.get_other_point(came_from) # Base case if next_point == root: return [], [path_to_follow], directions_taken # Find paths in next_point to discover other_paths = [] for n_path in self.edge_map.get(next_point.index, []): if n_path == path_to_follow: continue other_paths.append(n_path) if not other_paths: # Dead end return [], [], directions_taken elif len(other_paths) == 1: # Only one path to take first_path = other_paths[0] faces_found, constructed_path, directions_taken = self.take_path( root, first_path, next_point, directions_taken) if constructed_path: # Must only add if currently backtracking return faces_found, constructed_path + [path_to_follow ], directions_taken return [], [], directions_taken else: # Find segments to find angles between edges seg_start = find_path_segment(path_to_follow, next_point) seg_0 = find_path_segment(other_paths[0], next_point) seg_1 = find_path_segment(other_paths[1], next_point) # Assume path 1 has a smaller angle first_path = other_paths[1] second_path = other_paths[0] if segment_angle(seg_start, seg_0) < segment_angle( seg_start, seg_1): # Path 0 had a smaller angle first_path = other_paths[0] second_path = other_paths[1] # Take first path found_faces, constructed_path_first, directions_taken = self.take_path( root, first_path, next_point, directions_taken) if constructed_path_first: # Only start a new face if the first path also contains a face found_faces_second, constructed_path_second, directions_taken = self.take_path( next_point, second_path, next_point, directions_taken) found_faces.extend(found_faces_second) if constructed_path_second: found_faces.append(constructed_path_second) else: # Discard empty path from taking first found_faces, constructed_path_first, directions_taken = self.take_path( root, second_path, next_point, directions_taken) if constructed_path_first: return found_faces, constructed_path_first + [ path_to_follow ], directions_taken else: return found_faces, [], directions_taken
def convert_to_game_structure(graph: nx.Graph, embedding: nx.PlanarEmbedding, path_grouping): positions = scale_positions( nx.combinatorial_embedding_to_pos(embedding, True)) points = [GraphicsPoint] * len(graph.nodes) paths = [] # Initialize points for index, position in positions.items(): point = GraphicsPoint(*position, False) point.index = index points[index] = point # Initialize paths for path_start, path_end, mid_point in path_grouping: start_point = points[path_start[0]] end_point = points[path_end[1]] # The path was created from the same point to itself if start_point == end_point: end_point = points[path_start[1]] # Make small difference to width of the paths delta_pos = end_point - start_point mid_point = start_point + delta_pos.scalar(0.5) shift = delta_pos.rotate_anticlock().normalized().scalar(5) pos_control = mid_point + shift neg_control = mid_point - shift path_0 = Path([ GraphicsBezier(start_point, end_point, gc.LINE_COLOR, pos_control, pos_control) ], start_point, end_point) path_1 = Path([ GraphicsBezier(start_point, end_point, gc.LINE_COLOR, neg_control, neg_control) ], start_point, end_point) # Update paths of points start_point.add_to_path(path_0) start_point.add_to_path(path_1) end_point.add_to_path(path_0) end_point.add_to_path(path_1) paths.append((path_0, path_1, points[path_start[1]])) # Normal path else: middle_point = points[path_start[1]] path_0 = Path([GraphicsBezier(start_point, middle_point)], start_point, middle_point) path_1 = Path([GraphicsBezier(middle_point, end_point)], middle_point, end_point) # Update paths of points start_point.add_to_path(path_0) middle_point.add_to_path(path_0) middle_point.add_to_path(path_1) end_point.add_to_path(path_1) paths.append((path_0, path_1, middle_point)) # Test whether the points are still valid valid_point(start_point, start_point, end_point) valid_point(end_point, start_point, end_point) return points, paths