Experiment with coloring paths

This commit is contained in:
Griatch 2021-06-10 19:10:40 +02:00
parent c8b5db1b25
commit 0406e272d5
2 changed files with 74 additions and 100 deletions

View file

@ -1001,28 +1001,42 @@ class Map:
return directions, path
def get_map_display(self, coord, dist=2, mode='scan',
character='@', max_size=None, return_str=True):
def get_visual_range(self, coord, dist=2, mode='nodes',
character='@',
target=None, path_styler="|y{display_symbol}|n",
max_size=None,
return_str=True):
"""
Display the map centered on a point and everything around it within a certain distance.
Get a part of the grid centered on a specific point and extended a certain number
of nodes or grid points in every direction.
Args:
coord (tuple): (X,Y) in-world coordinate location. If this is not the location
of a node on the grid, the `character` or the empty-space symbol (by default
an empty space) will be shown.
dist (int, optional): Number of gridpoints distance to show. Which
grid to use depends on the setting of `only_nodes`.
grid to use depends on the setting of `only_nodes`. Set to `None` to
always show the entire grid.
mode (str, optional): One of 'scan' or 'nodes'. In 'scan' mode, dist measure
number of xy grid points in all directions and doesn't care about if visible
nodes are reachable or not. If 'nodes', distance measure how many linked nodes
away from the center coordinate to display.
character (str, optional): Place this symbol at the `coord` position
of the displayed map. The center node' symbol is shown if this is falsy.
target (tuple, optional): A target XY coordinate to go to. The path to this
(or the beginning of said path, if outside of visual range) will be
marked according to `path_style`.
path_styler (str or callable, optional): This is use for marking the path
found when `path_to_coord` is given. If a string, it accepts a formatting marker
`display_symbol` which will be filled with the `display_symbol` of each node/link
the path passes through. This allows e.g. to color the path. If a callable, this
will receive the MapNode or MapLink object for every step of the path and and
must return the suitable string to display at the position of the node/link.
max_size (tuple, optional): A max `(width, height)` to crop the displayed
return to. Make both odd numbers to get a perfect center.
If unset, display-size can grow up to the full size of the grid.
return_str (bool, optional): Return result as an
already formatted string.
return_str (bool, optional): Return result as an already formatted string
or a 2D list.
Returns:
str or list: Depending on value of `return_str`. If a list,
@ -1064,18 +1078,20 @@ class Map:
width, height = self.max_x + 1, self.max_y + 1
ix, iy = max(0, min(iX * 2, width)), max(0, min(iY * 2, height))
display_map = self.display_map
xmin, xmax, ymin, ymax = 0, width - 1, 0, height - 1
if dist <= 0 or not self.get_node_from_coord(coord):
if dist is None:
# show the entire grid
gridmap = self.display_map
ixc, iyc = ix, iy
elif dist is None or dist <= 0 or not self.get_node_from_coord(coord):
# There is no node at these coordinates. Show
# nothing but ourselves or emptiness
return character if character else self.empty_symbol
if mode == 'nodes':
elif mode == 'nodes':
# dist measures only full, reachable nodes.
# this requires a series of shortest-path
# Steps from on the pre-calulcated grid.
# from evennia import set_trace;set_trace()
points, xmin, xmax, ymin, ymax = self._get_topology_around_coord(coord, dist=dist)
ixc, iyc = ix - xmin, iy - ymin
@ -1086,83 +1102,41 @@ class Map:
for (ix0, iy0) in points:
gridmap[iy0 - ymin][ix0 - xmin] = display_map[iy0][ix0]
# if not self.dist_matrix:
# self._calculate_path_matrix()
#
# xmin, ymin = width, height
# xmax, ymax = 0, 0
# # adjusted center of map section
# ixc, iyc = ix, iy
#
# center_node = self.get_node_from_coord((iX, iY))
# if not center_node:
# # there is nothing at this grid location
# return character if character else ' '
#
# # the points list coordinates on the xygrid to show.
# points = [(ix, iy)]
# node_index_map = self.node_index_map
#
# # find all reachable nodes within a (weighted) distance of `dist`
# for inode, node_dist in enumerate(self.dist_matrix[center_node.node_index]):
#
# if node_dist > dist:
# continue
#
# # we have a node within 'dist' from us, get, the route to it
# node = node_index_map[inode]
# _, path = self.get_shortest_path((iX, iY), (node.X, node.Y))
# # follow directions to figure out which map coords to display
# node0 = node
# ix0, iy0 = ix, iy
# for path_element in path:
# # we don't need the start node since we know it already
# if isinstance(path_element, str):
# # a direction - this can lead to following
# # a longer link-chain chain
# for dstep in node0.xy_steps_to_noden[path_element]:
# dx, dy = _MAPSCAN[dstep]
# ix0, iy0 = ix0 + dx, iy0 + dy
# points.append((ix0, iy0))
# xmin, ymin = min(xmin, ix0), min(ymin, iy0)
# xmax, ymax = max(xmax, ix0), max(ymax, iy0)
# else:
# # a Mapnode
# node0 = path_element
# ix0, iy0 = node0.x, node0.y
# if (ix0, iy0) != (ix, iy):
# points.append((ix0, iy0))
# xmin, ymin = min(xmin, ix0), min(ymin, iy0)
# xmax, ymax = max(xmax, ix0), max(ymax, iy0)
#
# ixc, iyc = ix - xmin, iy - ymin
# # note - override width/height here since our grid is
# # now different from the original for future cropping
# width, height = xmax - xmin + 1, ymax - ymin + 1
# gridmap = [[" "] * width for _ in range(height)]
# for (ix0, iy0) in points:
# gridmap[iy0 - ymin][ix0 - xmin] = display_map[iy0][ix0]
elif mode == 'scans':
# scan-mode - dist measures individual grid points
xmin, xmax = max(0, ix - dist), min(width, ix + dist + 1)
ymin, ymax = max(0, iy - dist), min(height, iy + dist + 1)
ixc, iyc = ix - xmin, iy - ymin
gridmap = [line[xmin:xmax] for line in display_map[ymin:ymax]]
else:
# scan-mode (default) - dist measures individual grid points
if dist is None:
gridmap = self.display_map
ixc, iyc = ix, iy
else:
left, right = max(0, ix - dist), min(width, ix + dist + 1)
bottom, top = max(0, iy - dist), min(height, iy + dist + 1)
ixc, iyc = ix - left, iy - bottom
gridmap = [line[left:right] for line in display_map[bottom:top]]
raise MapError(f"Map.get_visual_range 'mode' was '{mode}' "
"- it must be either 'scan' or 'nodes'.")
if character:
gridmap[iyc][ixc] = character # correct indexing; it's a list of lines
if max_size:
# crop grid to make sure it doesn't grow too far
max_x, max_y = max_size
left, right = max(0, ixc - max_x // 2), min(width, ixc + max_x // 2 + 1)
bottom, top = max(0, iyc - max_y // 2), min(height, iyc + max_y // 2 + 1)
gridmap = [line[left:right] for line in gridmap[bottom:top]]
xmin, xmax = max(0, ixc - max_x // 2), min(width, ixc + max_x // 2 + 1)
ymin, ymax = max(0, iyc - max_y // 2), min(height, iyc + max_y // 2 + 1)
gridmap = [line[xmin:xmax] for line in gridmap[ymin:ymax]]
if target:
# stylize path to target
def _path_styler(node):
return path_styler
if not callable(path_styler):
path_styler = _path_styler
path, _ = self.get_shortest_path(coord, target)
for node_or_link in path[1:]:
ix, iy = node_or_link.x, node_or_link.y
if xmin <= ix <= xmax and ymin <= iy <= ymax:
gridmap[iy - ymin][ix - xmin] = path_styler(node_or_link)
if return_str:
# we must flip the y-axis before returning the string

View file

@ -270,13 +270,13 @@ class TestMap1(TestCase):
((1, 1), "-#\n |", [["-", "#"], [" ", "|"]]),
])
def test_get_map_display(self, coord, expectstr, expectlst):
def test_get_visual_range(self, coord, expectstr, expectlst):
"""
Test displaying a part of the map around a central point.
"""
mapstr = self.map.get_map_display(coord, dist=1, character=None)
maplst = self.map.get_map_display(coord, dist=1, return_str=False, character=None)
mapstr = self.map.get_visual_range(coord, dist=1, character=None)
maplst = self.map.get_visual_range(coord, dist=1, return_str=False, character=None)
self.assertEqual(expectstr, mapstr)
self.assertEqual(expectlst, maplst[::-1])
@ -287,14 +287,14 @@ class TestMap1(TestCase):
((1, 1), "-@\n |", [["-", "@"], [" ", "|"]]),
])
def test_get_map_display__character(self, coord, expectstr, expectlst):
def test_get_visual_range__character(self, coord, expectstr, expectlst):
"""
Test displaying a part of the map around a central point, showing the
character @-symbol in that spot.
"""
mapstr = self.map.get_map_display(coord, dist=1, character='@')
maplst = self.map.get_map_display(coord, dist=1, return_str=False, character='@')
mapstr = self.map.get_visual_range(coord, dist=1, character='@')
maplst = self.map.get_visual_range(coord, dist=1, return_str=False, character='@')
self.assertEqual(expectstr, mapstr)
self.assertEqual(expectlst, maplst[::-1]) # flip y-axis to match print direction
@ -306,12 +306,12 @@ class TestMap1(TestCase):
((0, 0), 2, '#-#\n| |\n@-#'),
])
def test_get_map_display__nodes__character(self, coord, dist, expected):
def test_get_visual_range__nodes__character(self, coord, dist, expected):
"""
Get sub-part of map with node-mode.
"""
mapstr = self.map.get_map_display(coord, dist=dist, mode='nodes', character='@')
mapstr = self.map.get_visual_range(coord, dist=dist, mode='nodes', character='@')
self.assertEqual(expected, mapstr)
class TestMap2(TestCase):
@ -360,12 +360,12 @@ class TestMap2(TestCase):
((4, 5), '#-#-@ \n| | \n#---# \n| | \n| #-#'),
((5, 2), '--# \n | \n #-#\n |\n#---@\n \n--#-#\n | \n#-# '),
])
def test_get_map_display__scan__character(self, coord, expected):
def test_get_visual_range__scan__character(self, coord, expected):
"""
Test showing smaller part of grid, showing @-character in the middle.
"""
mapstr = self.map.get_map_display(coord, dist=4, character='@')
mapstr = self.map.get_visual_range(coord, dist=4, character='@')
self.assertEqual(expected, mapstr)
def test_extended_path_tracking__horizontal(self):
@ -412,13 +412,13 @@ class TestMap2(TestCase):
((2, 2), 4, (3, 3), ' | \n-@-\n | '),
((2, 2), 4, (1, 1), '@')
])
def test_get_map_display__nodes__character(self, coord, dist, max_size, expected):
def test_get_visual_range__nodes__character(self, coord, dist, max_size, expected):
"""
Get sub-part of map with node-mode.
"""
mapstr = self.map.get_map_display(coord, dist=dist, mode='nodes', character='@',
max_size=max_size)
mapstr = self.map.get_visual_range(coord, dist=dist, mode='nodes', character='@',
max_size=max_size)
self.assertEqual(expected, mapstr)
@ -462,13 +462,13 @@ class TestMap3(TestCase):
'\n # @-# \n |/ \\ \n # #\n / \\ \n# # '),
((5, 2), 2, None, ' # \n | \n # \n / \\ \n# @\n \\ / \n # \n | \n # ')
])
def test_get_map_display__nodes__character(self, coord, dist, max_size, expected):
def test_get_visual_range__nodes__character(self, coord, dist, max_size, expected):
"""
Get sub-part of map with node-mode.
"""
mapstr = self.map.get_map_display(coord, dist=dist, mode='nodes', character='@',
max_size=max_size)
mapstr = self.map.get_visual_range(coord, dist=dist, mode='nodes', character='@',
max_size=max_size)
print(f"\n\n{expected}\n\n{mapstr}\n\n{repr(mapstr)}")
self.assertEqual(expected, mapstr)
@ -624,12 +624,12 @@ class TestMap8(TestCase):
((2, 2), 1, None, ' #-o \n | \n# o \n| | \no-o-@-#\n '
'| \n o \n | \n # '),
])
def test_get_map_display__nodes__character(self, coord, dist, max_size, expected):
def test_get_visual_range__nodes__character(self, coord, dist, max_size, expected):
"""
Get sub-part of map with node-mode.
"""
mapstr = self.map.get_map_display(coord, dist=dist, mode='nodes', character='@',
max_size=max_size)
mapstr = self.map.get_visual_range(coord, dist=dist, mode='nodes', character='@',
max_size=max_size)
print(repr(mapstr))
self.assertEqual(expected, mapstr)