コード例 #1
0
ファイル: datastructure.py プロジェクト: bmmeijers/tgap-ng
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
コード例 #2
0
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 = []
コード例 #3
0
	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 = []
コード例 #4
0
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
コード例 #5
0
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()