Extra: Viz¶
There are a few utility functions used throughout this jupyter notebook to visualize our grid. We will discuss them briefly here.
%reload_ext autoreload
%autoreload 2
import sys
import numpy as np
from pathfinding import grid, utils, finder
from pathfinding.grid import core, viz
# we do this so np print won't truncate
np.set_printoptions(threshold=sys.maxsize)
# let's alias core.find_walkable_neighbors to fwn for brevity
fwn = core.find_walkable_neighbors
Text¶
The first one we see is generate_text
, which pretty prints our grid.
polygons = [[(14, 5), (14, 7), (16, 7), (16, 5)]]
maze, start, end = grid.generate(20,10, polygons), (2, 2), (20 - 3, 10 - 3)
path, expansion = finder.bfs(maze, fwn, start, end, with_expansion=True)
print(viz.generate_text(maze, start, end, path))
For generate_text
, we only copy the grid/list and replae the values with our own visual text representation (i.e. space for 1)
def generate_text_map(x):
if x == 0:
return "="
elif x == 1:
return " "
if x == 4:
return "*"
if x == 5:
return "@"
elif x == 2:
return "S"
elif x == 3:
return "E"
else:
return str(x)
def generate_text(grid, start, end, path=[], expansion=[]):
maze = np.copy(grid)
for coord in expansion:
maze[coord[1]][coord[0]] = 5
for coord in path:
maze[coord[1]][coord[0]] = 4
maze[start[1]][start[0]] = 2
maze[end[1]][end[0]] = 3
return np.array2string(maze, formatter={'int': generate_text_map})
Image¶
Image is a bit more involved, but not much harder. Here, we simply create a RGB map from our grid/maze data, and create an image from our rgb array.
def generate_image(grid, start, end, path=[], expansion=[]):
r = g = b = (grid * 255).astype(np.uint8)
img_array = np.empty((grid.shape[0], grid.shape[1], 3), dtype=np.uint8)
img_array[..., 0] = r
img_array[..., 1] = g
img_array[..., 2] = b
for coord in expansion:
img_array[coord[1], coord[0]] = [200, 200, 200]
for coord in path:
img_array[coord[1], coord[0]] = [254, 254, 59]
# display start
img_array[start[1], start[0]] = [0, 255, 0]
# display end
img_array[end[1], end[0]] = [255, 0, 0]
return img_array
def render_image(img_array, display_size=(1000, 500)):
return display(Image.fromarray(img_array, 'RGB').resize(display_size))
polygons = [[(14, 5), (14, 7), (16, 7), (16, 5)]]
maze, start, end = grid.generate(20,10, polygons), (2, 2), (20 - 3, 10 - 3)
path, expansion = finder.bfs(maze, fwn, start, end, with_expansion=True)
utils.render_image(viz.generate_image(maze, start, end, path))
Animation¶
Animation involves the most 'hacks' but not by much. Here, we simply use matplotlib
's FuncAnimation
and loop through drawing patches.Rectangle
Note that since most of our algorithms have really large number of nodes expanded, we have to be smart and bundle some nodes together in frames, otherwise it would take a really long time to generate an animation (hence the frames
argument)
def generate_anim(grid, start, end, path=[], expansion=[], frames=15):
fig, ax = plt.subplots()
fig.subplots_adjust(left=0, right=1, bottom=0, top=1)
img = generate_image(grid, start, end, path, expansion[:1])
ax.imshow(img)
squares = [Rectangle((coord[0] - 0.5, coord[1] - 0.5), 1, 1, color='black') for coord in expansion]
for sq in squares:
sq.set_alpha(0)
ax.add_patch(sq)
multiple = int(len(expansion) / frames)
def init():
return squares
def update(i):
for j in range(i * multiple, (i + 1) * multiple):
squares[j].set_alpha(0.3)
return squares
return animation.FuncAnimation(fig, update, frames, blit=True, interval=100, init_func=init)
polygons = [[(14, 5), (14, 7), (16, 7), (16, 5)]]
maze, start, end = grid.generate(20,10, polygons), (2, 2), (20 - 3, 10 - 3)
path, expansion = finder.bfs(maze, fwn, start, end, with_expansion=True)
utils.render_anim(viz.generate_anim(maze, start, end, path, expansion, frames=len(expansion)))
This is a post in the CS 180 Pathfinding series.
Other posts in this series:
- Mar 17, 2019 - CS 180 Pathfinding
- Mar 17, 2019 - Grid
- Mar 17, 2019 - Finders
- Mar 17, 2019 - Grid Search
- Mar 17, 2019 - Test Cases
- Mar 17, 2019 - Running Test Cases
- Mar 17, 2019 - Timing
- Mar 17, 2019 - Viz