def retrieve(DATASET, SRID, unbounded_id): pp = PlanarPartition(unbounded_id) sql = ( "select st_setsrid(st_extent(geometry)::geometry(polygon)," + str(SRID) + ") from " + DATASET + "_edge;" ) with connection(True) as db: mbr_geometry, = db.record(sql) dataset_envelope = mbr_geometry.envelope # unbounded face, otherwise this face cannot be found pp.faces[unbounded_id] = Face(unbounded_id, dataset_envelope, None, set([]), {}) pp.face_hierarchy[unbounded_id] = None faces = pp.faces face_hierarchy = pp.face_hierarchy edge_hierarchy = pp.edge_hierarchy t0 = time.time() sql = ( "select face_id::int, feature_class::int, mbr_geometry, pip_geometry from " + DATASET + "_face" ) # where imp_low = 0" # sql = "select id::int, height::int, null as mbr_geometry, null as pip_geometry from "+DATASET+"_face" # where imp_low = 0" with connection(True) as db: for item in db.irecordset(sql): face_id, feature_class_id, mbr_geometry, pip_geometry, = item faces[face_id] = Face( face_id, mbr_geometry.envelope, # FIXME store as box2d and map to Envelope at connection level ??? # mbr_geometry, pip_geometry, set([]), {"feature_class_id": feature_class_id, "step_low": 0}, ) face_hierarchy[face_id] = None print(f"{time.time()-t0:.3f}s face retrieval: {len(faces)} faces") t0 = time.time() edges = pp.edges # distinct, as the drenthe dataset has edges twice (with multiple feature class) sql = ( "select distinct edge_id::int, start_node_id::int, end_node_id::int, left_face_id::int, right_face_id::int, geometry from " + DATASET + "_edge order by edge_id" ) # where imp_low = 0" # sql = "select id::int, startnode::int, endnode::int, leftface::int, rightface::int, geometry from "+DATASET+"_edge" # where imp_low = 0" # from tmp_mut_kdtree import create as create_kdtree # kdtree = create_kdtree(dimensions=2) # from tmp_grid import Grid # from math import ceil # kdtree = Grid(sizes = (ceil(dataset_envelope.width/50.), ceil(dataset_envelope.height/50.))) ### get unique vertices by hashing the geometry pts = {} with connection(True) as db: for item in db.irecordset(sql): edge_id, start_node_id, end_node_id, left_face_id, right_face_id, geometry, = ( item ) # pp.edge_rtree.add(edge_id, geometry.envelope) edges[edge_id] = Edge( edge_id, start_node_id, angle(geometry[0], geometry[1]), end_node_id, angle(geometry[-1], geometry[-2]), left_face_id, right_face_id, geometry, {"step_low": 0} # {'smooth': make_smooth_line(geometry)}) ) # check for dup points for j in range(1, len(geometry)): i = j - 1 assert geometry[i] != geometry[j], geometry # DO_SIMPLIFY for pt in geometry: if (pt.x, pt.y) not in pts: pts[(pt.x, pt.y)] = [edge_id] else: pts[(pt.x, pt.y)].append(edge_id) # add the edge_ids to the edges sets of the faces # on the left and right of the edge faces[left_face_id].edges.add(edge_id) faces[right_face_id].edges.add(~edge_id) edge_hierarchy[edge_id] = None print(f"{time.time()-t0:.3f}s edge retrieval: {len(edges)} edges") t0 = time.time() # DO_SIMPLIFY tree = QuadTree( [ (dataset_envelope.xmin, dataset_envelope.ymin), (dataset_envelope.xmax, dataset_envelope.ymax), ], 64, ) for pt in pts.keys(): tree.add(pt) pp.quadtree = tree print(f"{time.time()-t0:.3f}s quadtree construction") t0 = time.time() ct = 0 for pt in pp.quadtree: ct += 1 print(f"spatially indexed {ct} points (quadtree)") # from .tmp_kdtree import create as create_kdtree ## from .tmp_mut_kdtree import create as create_kdtree # pp.kdtree = create_kdtree(point_list=pts.keys(), dimensions=2) # pp.kdtree = None # def output_points(pts, fh): # for pt in pts: # fh.write("POINT({0[0]} {0[1]})\n".format(pt)) # with open('/tmp/kdtree_pts.wkt', 'w') as fh: # fh.write('wkt\n') # pts = pp.kdtree.range_search( (float('-inf'), float('-inf')), (float('+inf'), float('+inf')) ) ## output_points((node.data for node in pp.kdtree.inorder()), fh) ## output_points(pts, fh) ## raw_input('paused') # check if we did not mix up things for edge_id in edges: edge = edges[edge_id] assert edge_id in faces[edge.left_face_id].edges assert ~edge_id in faces[edge.right_face_id].edges # stars # stars = pp.stars # for edge_id in edges: # stars[edges[edge_id].start_node_id].append(edge_id) # outgoing: + # stars[edges[edge_id].end_node_id].append(~edge_id) # incoming: - # nodes nodes = pp.nodes for edge_id in edges: edge = edges[edge_id] # start node if edge.start_node_id not in nodes: nodes[edge.start_node_id] = Node(edge.start_node_id, edge.geometry[0], []) nodes[edge.start_node_id].star.append(edge_id) # end node if edge.end_node_id not in nodes: nodes[edge.end_node_id] = Node(edge.end_node_id, edge.geometry[-1], []) nodes[edges[edge_id].end_node_id].star.append(~edge_id) # sort edges counter clockwise (?) around a node sort_on_angle = partial(get_correct_angle, edges=edges) # for node_id in stars.keys(): # stars[node_id].sort(key=sort_on_angle) for node_id in nodes.keys(): nodes[node_id].star.sort(key=sort_on_angle) print(f"{time.time()-t0:.3f}s node stars") t0 = time.time() # based on the wheels we find, we can obtain the size of the faces for face in faces.values(): wheels = get_wheel_edges(face.edges, pp) rings = [get_geometry_for_wheel(wheel, pp) for wheel in wheels] rings = [(abs(ring.signed_area()), ring) for ring in rings] rings.sort(reverse=True, key=lambda x: x[0]) area, largest_ring = rings[0] perimeter = largest_ring.length iso_perimetric_quotient = (4.0 * math.pi * area) / (perimeter * perimeter) # FIXME: should we subtract hole regions from the faces? face.info["area"] = area face.info["perimeter"] = perimeter face.info["ipq"] = iso_perimetric_quotient face.info["priority"] = face.info["area"] * face.info["ipq"] print(f"{time.time()-t0:.3f}s area calculation") # when we ask the rtree the whole domain, we should get all edges # assert len(pp.edge_rtree.intersection(dataset_envelope)) == len(edges) # return the planar partition return pp
quadtree = QuadTree(8, 640, 640) keep_going = True while keep_going: for event in pygame.event.get(): if event.type == pygame.QUIT: keep_going = False if event.type == pygame.KEYDOWN: if event.key == pygame.K_a: x = random.randint(0, 600) y = random.randint(0, 600) w = random.randint(3, 40) h = random.randint(3, 40) color = (random.randint(64, 250), random.randint(64, 250), random.randint(64, 250)) quadtree.add(RectData(x, y, w, h, color)) mx1, my1, mx2, my2 = (0, 0, 0, 0) mdown = False selected = [] if event.key == pygame.K_b: for d in selected: quadtree.remove(d) selected = [] if event.key == pygame.K_c: quadtree.clear() mx1, my1, mx2, my2 = (0, 0, 0, 0) mdown = False selected = []
quadtree = QuadTree(8, 640, 640) keep_going = True while keep_going: for event in pygame.event.get(): if event.type == pygame.QUIT: keep_going = False if event.type == pygame.KEYDOWN: if event.key == pygame.K_RETURN: x = random.randint(0,600) y = random.randint(0,600) w = random.randint(3,40) h = random.randint(3,40) color = (random.randint(64,250), random.randint(64,250), random.randint(64,250)) quadtree.add(RectData(x, y, w, h, color)) mx1,my1,mx2,my2 = (0,0,0,0) mdown = False selected = [] if event.key == pygame.K_BACKSPACE: for d in selected: quadtree.remove(d) selected = [] if event.key == pygame.K_DELETE: quadtree.clear() mx1,my1,mx2,my2 = (0,0,0,0) mdown = False selected = []
class DrawTree(pantograph.PantographHandler): def setup(self): self.quadtree = QuadTree(8, self.width, self.height) self.buffer = 15 self.numRectangles = 100 self.numPoints = 100 self.rectangles = [] self.points = [] self.rectColor = "#354F00" self.pointColor = "#567714" self.backColor = "#97A084" self.collRectColor = "#441154" self.maxRectSize = 100 self.minRectSize = 20 self.pointSize = 5 self.collRectWidth = 150 self.collRectHeight = 150 self.collRect = RectData(0,0,self.collRectWidth,self.collRectHeight,"#F00") for i in range(self.numRectangles): vals = self.genRandomVals("rectangle") rect = RectData(vals.x, vals.y, vals.w, vals.h, self.rectColor) self.rectangles.append(rect) self.quadtree.add(rect) for i in range(self.numPoints): vals = self.genRandomVals("point") point = RectData(vals.x, vals.y, vals.w, vals.h, self.pointColor) self.points.append(point) self.quadtree.add(point) self.draw_rect(0, 0, self.width, self.height, "#000") self.draw_rect(self.collRect.x,self.collRect.y,self.collRect.w,self.collRect.h,self.collRect.data) def update(self): self.clear_rect(0, 0, self.width, self.height) self.drawShapes() self.selected = [rect for rect in self.quadtree.querry(self.collRect.x,self.collRect.y,self.collRect.w,self.collRect.h)] self.drawSelected() self.moveCollRect() def drawSelected(self): for rect in self.selected: self.draw_rect(rect.x, rect.y, rect.w, rect.h, "#FFA500") def moveCollRect(self): self.collRect.x += 5 if self.collRect.x >= self.width: self.collRect.x = 0 self.collRect.y = (self.collRect.y + self.collRectHeight) % self.height def drawShapes(self): for rect in self.rectangles: self.draw_rect(rect.x, rect.y, rect.w, rect.h, rect.data) for point in self.points: self.fill_rect(point.x, point.y, point.w, point.h, point.data) self.draw_rect(0, 0, self.width, self.height, "#000") self.draw_rect(self.collRect.x,self.collRect.y,self.collRect.w,self.collRect.h,self.collRect.data) def genRandomVals(self,shape="rectangle"): x = random.randint(0,self.width-(self.buffer*2)) y = random.randint(0,self.height-(self.buffer*2)) h = random.randint(self.minRectSize,self.maxRectSize) w = random.randint(self.minRectSize,self.maxRectSize) if (y+h) > self.height: h = (self.height-self.buffer) if (x+w) > self.width: w = (self.width-self.buffer) if shape == "point": h = self.pointSize w = self.pointSize return Size(x,y,w,h) def on_mouse_down(self,InputEvent): self.collRect.x = InputEvent.x self.collRect.y = InputEvent.y def on_mouse_move(self,InputEvent): self.collRect.x = InputEvent.x self.collRect.y = InputEvent.y
class PathWalker: def __init__(self, save_file = None): self.SAVE_FILE = "walls.json" self.SCREEN_SIZE = (1000, 625) self.VERTICE_SIZE = 3 self.HIT_SIZE = 6 self.BACKGROUND_COLOR = pygame.Color('white') self.WALL_COLOR = ( 0, 0, 0 ) self.WALL_THICKNESS = 3 self.NEW_WALL_SEGMENT_COLOR = ( 192, 192, 192 ) self.NEW_WALL_THICKNESS = 1 self.VERTICE_COLOR = ( 220, 220, 255 ) self.EDGE_COLOR = ( 240, 240, 255 ) self.QUAD_TREE_RECT_COLOR = ( 224, 232, 224 ) self.WALKER_COLOR = ( 64, 192, 64 ) self.WALKER_SIZE = 8 self.WALKER_COMFORT_ZONE = self.WALKER_SIZE + 2 self.WALKER_VERTICE_BUILDING_COMFORT_ZONE = self.WALKER_COMFORT_ZONE + 1 self.REDRAW_RATE = 90 self.alt_pressed = False self.shift_pressed = False self.ctrl_pressed = False self.show_pathfinding_graph = False self.show_quad_tree_rects = False self.walls = [] self.quad_tree = None self.new_wall = None self.dragged_corner = None self.vertices = [] self.edges = [] self.quad_tree_rects = [] self.walker_position = geometry.Point( 200, 200 ) self.walker_path = None self.walker_path_position = None self.walker_speed_p_s = 160 self.walker_last_move_time = None self.load_walls() self.redraw = True def load_walls(self): with open( self.SAVE_FILE ) as f: json_file = json.load( f ) if "walls" in json_file: self.walls = [ [ geometry.Point( int( corner[0] ), int( corner[1] ) ) for corner in wall ] for wall in json_file["walls"] ] if "screen_size" in json_file: self.SCREEN_SIZE = json_file["screen_size"] # self.rebuild_pathfinding_graph() def save_walls(self): with open( self.SAVE_FILE, "w" ) as f: json.dump( { "screen_size" : self.SCREEN_SIZE, "walls": [ [ ( corner.x, corner.y ) for corner in wall ] for wall in self.walls ] }, f ) def redraw_screen(self, surface): surface.fill(self.BACKGROUND_COLOR) if self.show_quad_tree_rects: for rect in self.quad_tree_rects: pygame.draw.rect( surface, self.QUAD_TREE_RECT_COLOR, rect, 1 ) if self.show_pathfinding_graph: for edge in self.edges: start = self.vertices[ edge[0] ].int_tuple( ) end = self.vertices[ edge[1] ].int_tuple( ) pygame.draw.line( surface, self.EDGE_COLOR, start, end ) for vertice in self.vertices: pygame.draw.circle( surface, self.VERTICE_COLOR, vertice.int_tuple( ), self.VERTICE_SIZE ) for wall in self.walls: for wall_segment in pairwise( wall ): pygame.draw.line( surface, self.WALL_COLOR, wall_segment[0].int_tuple( ), wall_segment[1].int_tuple( ), self.WALL_THICKNESS ) if self.new_wall is not None: for i in range( 1, len( self.new_wall ) - 1 ): pygame.draw.line(surface, self.WALL_COLOR, self.new_wall[i-1].int_tuple(), self.new_wall[i].int_tuple(), self.NEW_WALL_THICKNESS) pygame.draw.line(surface, self.NEW_WALL_SEGMENT_COLOR, self.new_wall[-2].int_tuple(), self.new_wall[-1].int_tuple(), self.NEW_WALL_THICKNESS) pygame.draw.circle(surface, self.WALKER_COLOR, ( int( self.walker_position.x ), int( self.walker_position.y ) ), self.WALKER_SIZE) pygame.display.flip() def generate_vertices(self): offset = self.WALKER_VERTICE_BUILDING_COMFORT_ZONE for wall in self.walls: if len( wall ) > 2 and wall[0] == wall[-1]: self.vertices += vertices_for_corner( wall[1], wall[0], wall[-2], offset ) else: self.vertices += vertices_for_line_segment_end( wall[0], wall[1], offset ) self.vertices += vertices_for_line_segment_end( wall[-1], wall[-2], offset ) for wall_corner in pairwise( pairwise( wall ) ): self.vertices += vertices_for_corner( wall_corner[0][0], wall_corner[0][1], wall_corner[1][1], offset ) def rebuild_quad_tree(self): self.quad_tree = QuadTree( geometry.BoundingBox( 0, self.SCREEN_SIZE[0], 0, self.SCREEN_SIZE[1] ), max_elems=4 ) for wall in self.walls: for wall_segment in pairwise( wall ): self.quad_tree.add( geometry.LineSegment( wall_segment[0], wall_segment[1] ) ) self.quad_tree_rects = [] def get_quadrants_rects( quadrant ): if quadrant.subquadrants is not None: for subquadrant in quadrant.subquadrants: get_quadrants_rects( subquadrant ) else: self.quad_tree_rects.append( pygame.Rect( quadrant.bounding_box.x_lower, quadrant.bounding_box.y_lower, quadrant.bounding_box.x_upper - quadrant.bounding_box.x_lower + 1, quadrant.bounding_box.y_upper - quadrant.bounding_box.y_lower + 1 ) ) get_quadrants_rects( self.quad_tree.main_quadrant ) def rebuild_pathfinding_graph(self): self.quad_tree = None self.vertices = [] self.edges = [] start_time = time.time() self.generate_vertices() vertices_end_time = time.time() self.rebuild_quad_tree() quad_tree_end_time = time.time( ) self.generate_edges() edges_end_time = time.time() print( "rebuilding pathfinding graph took {} seconds ({} for vertices, {} for quad tree and {} for edges)".format( edges_end_time-start_time, vertices_end_time-start_time, quad_tree_end_time-vertices_end_time, edges_end_time-quad_tree_end_time ) ) def add_new_wall(self, wall): if len( wall ) == 3 and wall[0] == wall[-1]: wall = wall[0:2] self.walls.append( wall ) def is_line_walkable(self, walk_line_segment): if self.quad_tree is None: return False start_circle = geometry.Circle( walk_line_segment.start, self.WALKER_COMFORT_ZONE ) end_circle = geometry.Circle( walk_line_segment.end, self.WALKER_COMFORT_ZONE ) start_points = geometry.intersect_line_and_circle( start_circle, walk_line_segment.perpendicular( walk_line_segment.start ) ) end_points = geometry.intersect_line_and_circle( end_circle, walk_line_segment.perpendicular( walk_line_segment.end ) ) if not walk_line_segment.are_on_the_same_side( start_points[0], end_points[0] ): end_points[0], end_points[1] = end_points[1], end_points[0] trajectory_bounding_rect_segments = [ geometry.LineSegment( start_points[0], start_points[1] ), geometry.LineSegment( start_points[1], end_points[1] ), geometry.LineSegment( end_points[1], end_points[0] ), geometry.LineSegment( end_points[0], start_points[0] ) ] potentially_interfering_walls = [] for bounding_box in get_line_segment_bounding_boxes( walk_line_segment ): potentially_interfering_walls += self.quad_tree.get( bounding_box.expand( self.WALKER_COMFORT_ZONE + 1 ) ) for wall_line_segment in potentially_interfering_walls: if ( does_line_segment_interfere_with_circle( wall_line_segment, start_circle ) or does_line_segment_interfere_with_circle( wall_line_segment, end_circle ) ): return False if does_line_segment_interfere_with_rect( wall_line_segment, trajectory_bounding_rect_segments ): return False return True def generate_edges(self): for i in range( len( self.vertices ) ): for j in range( i+1, len( self.vertices ) ): new_edge = geometry.LineSegment( self.vertices[ i ], self.vertices[ j ] ) if self.is_line_walkable( new_edge ): self.edges.append( (i, j) ) def handle_alt_shift_ctrl(self, event): if event.type == pygame.KEYDOWN: if event.key == pygame.K_RSHIFT or event.key == pygame.K_LSHIFT: self.shift_pressed = True return True elif event.key == pygame.K_RALT or event.key == pygame.K_LALT: self.alt_pressed = True return True elif event.key == pygame.K_RCTRL or event.key == pygame.K_LCTRL: self.ctrl_pressed = True return True elif event.type == pygame.KEYUP: if event.key == pygame.K_RSHIFT or event.key == pygame.K_LSHIFT: if self.new_wall is not None: if len( self.new_wall ) > 2: self.add_new_wall( self.new_wall[:-1] ) self.new_wall = None self.redraw = True self.shift_pressed = False return True elif event.key == pygame.K_RALT or event.key == pygame.K_LALT: self.alt_pressed = False return True elif event.key == pygame.K_RCTRL or event.key == pygame.K_LCTRL: self.ctrl_pressed = False return True return False def handle_mouse_move(self, event): if self.new_wall is not None: if check_hit( self.new_wall[0].int_tuple( ), event.pos, self.HIT_SIZE ): self.new_wall[-1] = self.new_wall[0] else: for wall in self.walls: if wall[0] == wall[-1]: continue if check_hit( wall[0].int_tuple( ), event.pos, self.HIT_SIZE ): self.new_wall[-1] = wall[0] break if check_hit(wall[-1].int_tuple(), event.pos, self.HIT_SIZE): self.new_wall[-1] = wall[-1] break else: self.new_wall[-1] = geometry.Point( *event.pos ) self.redraw = True elif self.dragged_corner is not None: wall = self.walls[ self.dragged_corner[0] ] if self.dragged_corner[1] == 0 and wall[0] == wall[-1]: wall[0] = wall[-1] = geometry.Point( *event.pos ) else: wall[ self.dragged_corner[1] ] = geometry.Point( *event.pos ) self.redraw = True def handle_mouse_down(self, event): if event.button == LEFT_MOUSE_BUTTON: if self.alt_pressed: pass elif self.ctrl_pressed: for i in range( len( self.walls ) ): for j in range( len( self.walls[i] ) ): if check_hit(self.walls[i][j].int_tuple(), event.pos, self.HIT_SIZE): if j == 0 and self.walls[i][0] == self.walls[i][-1]: # this is closed wall and we're removing first/last - "unclose" the wall self.walls[i].pop(0) self.walls[i].pop(-1) elif j == 0 or j == len( self.walls[i] ) - 1: # first or last - just remove it self.walls[i].pop(j) if len( self.walls[i] ) < 2: self.walls.pop( i ) else: # remove an element in the middle if self.walls[i][0] == self.walls[i][-1]: # if it is "closed" - unclose it self.walls[i] = self.walls[i][j+1:-1] + self.walls[i][0:j] else: # not "closed" wall - break it into two if len( self.walls[i] ) - j - 1 >= 2: self.walls.append( self.walls[i][j+1:] ) if j >= 2: self.walls[i] = self.walls[i][0:j] else: self.walls.pop( i ) self.redraw = True return elif self.shift_pressed: if self.new_wall is None: for i in range( len( self.walls ) ): if check_hit( self.walls[i][0].int_tuple(), event.pos, self.HIT_SIZE ): self.new_wall = list( reversed( self.walls.pop( i ) ) ) self.new_wall.append( geometry.Point( *event.pos ) ) break elif check_hit( self.walls[i][-1].int_tuple(), event.pos, self.HIT_SIZE ): self.new_wall = self.walls.pop( i ) self.new_wall.append( geometry.Point( *event.pos ) ) break else: self.new_wall = [ geometry.Point( *event.pos ), geometry.Point( *event.pos ) ] else: if check_hit(self.new_wall[0].int_tuple(), event.pos, self.HIT_SIZE): self.new_wall[-1] = self.new_wall[0] self.add_new_wall(self.new_wall) self.new_wall = None else: for i in range( len( self.walls ) ): if self.walls[i][0] == self.walls[i][-1]: continue if check_hit(self.walls[i][0].int_tuple(), event.pos, self.HIT_SIZE): self.walls[i] = self.new_wall[:-1] + self.walls[i] self.new_wall = None break if check_hit(self.walls[i][-1].int_tuple(), event.pos, self.HIT_SIZE): self.walls[i] = self.new_wall[:-1] + list( reversed( self.walls[i] ) ) self.new_wall = None break else: self.new_wall[-1] = geometry.Point(*event.pos) if self.new_wall[-2] != self.new_wall[-1]: self.new_wall.append( geometry.Point( *event.pos ) ) self.redraw = True else: for i in range( len( self.walls ) ): for j in range( len( self.walls[i] ) ): if check_hit(self.walls[i][j].int_tuple(), event.pos, self.HIT_SIZE): self.dragged_corner = ( i, j ) elif event.button == RIGHT_MOUSE_BUTTON: if self.alt_pressed: pass elif self.ctrl_pressed: print( "teleport", event.pos ) self.stop_walk() self.walker_position = geometry.Point( *event.pos ) self.redraw = True elif self.shift_pressed: pass else: if self.walker_position == geometry.Point( *event.pos ): return start_time = time.time( ) new_path = self.find_walk_path( geometry.Point( *event.pos ) ) end_time = time.time( ) print( "finding path took {} seconds".format( end_time-start_time ) ) if new_path is not None: self.walker_path = new_path self.walker_path_position = 0 self.walker_last_move_time = time.time( ) else: self.stop_walk() def find_walk_path(self, dest): if self.is_line_walkable( geometry.LineSegment( self.walker_position, dest ) ): return [ self.walker_position, dest ] vertices = self.vertices + [ self.walker_position, dest ] vertice_count = len( vertices ) edges = self.edges.copy( ) for i in range( vertice_count - 2 ): if self.is_line_walkable( geometry.LineSegment( vertices[ i ], self.walker_position ) ): edges.append( ( i, vertice_count - 2 ) ) if self.is_line_walkable( geometry.LineSegment( vertices[ i ], dest ) ): edges.append( ( i, vertice_count - 1 ) ) graph = { i : dict( ) for i in range( len( vertices ) ) } for edge in edges: distance = geometry.distance( vertices[ edge[0] ], vertices[ edge[1] ] ) graph[ edge[0] ][ edge[1] ] = distance graph[ edge[1] ][ edge[0] ] = distance start_time = time.time() path = pathfinder.find_cheapest_path_dijkstra( graph, vertice_count-2, vertice_count-1 ) end_time = time.time() print( "dijkstra algorithm took {} seconds".format( end_time - start_time ) ) if path is None: return None path_coordinates = [ vertices[ vertice_i ] for vertice_i in path["path"] ] return path_coordinates def stop_walk(self): self.walker_path = None self.walker_path_position = None self.walker_last_move_time = None def walk(self): if self.walker_path is None: return if self.walker_last_move_time is None: self.walker_last_move_time = time.time( ) return current_time = time.time( ) self.walker_path_position += ( current_time - self.walker_last_move_time ) * self.walker_speed_p_s current_leg_length = geometry.distance( self.walker_path[0], self.walker_path[1] ) if self.walker_path_position >= current_leg_length: if len( self.walker_path ) == 2: self.walker_position = self.walker_path[1] self.stop_walk( ) return else: self.walker_path_position -= current_leg_length self.walker_path.pop( 0 ) self.walker_position = geometry.measure_out( self.walker_path[0], self.walker_path[1], self.walker_path_position ) self.walker_last_move_time = current_time self.redraw = True def handle_mouse_up(self, event): if self.dragged_corner is not None: self.dragged_corner = None self.redraw = True def run(self): print( """brief help: shift-click: add wall click+drag: move corner ctrl-click: remove corner right-click: walk ctrl-right-click: teleport F2 - show/hide pathfinding graph F3 - show/hide quad tree rects F12 - rebuild pathfinding graph """ ) pygame.init() pygame.display.set_caption("path walker") screen = pygame.display.set_mode( self.SCREEN_SIZE ) self.redraw_screen(screen) done = False while not done: self.redraw = False for event in pygame.event.get(): if event.type == pygame.QUIT: done = True elif event.type == pygame.MOUSEMOTION: self.handle_mouse_move( event ) elif event.type == pygame.MOUSEBUTTONDOWN: self.handle_mouse_down( event ) elif event.type == pygame.MOUSEBUTTONUP: self.handle_mouse_up( event ) elif self.handle_alt_shift_ctrl( event ): pass elif event.type == pygame.KEYDOWN: if event.key == pygame.K_F2: self.show_pathfinding_graph = not self.show_pathfinding_graph self.redraw = True elif event.key == pygame.K_F3: self.show_quad_tree_rects = not self.show_quad_tree_rects self.redraw = True elif event.key == pygame.K_F12: self.rebuild_pathfinding_graph() self.redraw = True if done: break self.walk() if not self.redraw: time.sleep( 1/self.REDRAW_RATE ) continue screen.fill(self.BACKGROUND_COLOR) self.redraw_screen( screen ) pygame.display.flip() self.save_walls() pygame.quit()