change the poorly integrated "walkable-tile-check"

This commit is contained in:
Steffen Illium 2017-07-24 11:24:30 +02:00
parent 11fc99cbb5
commit bdc3ee46d6
2 changed files with 43 additions and 42 deletions

View File

@ -11,7 +11,7 @@ from tools import Worker, Isovist, TrackCollection, IndoorToolset # , Track, Is
if __name__ == '__main__': if __name__ == '__main__':
walkableTiles = 255 walkableTiles = [255, 103, 210, 79]
trackColor = 103 trackColor = 103
startColor = 210 startColor = 210
endColor = 79 endColor = 79
@ -82,7 +82,7 @@ if __name__ == '__main__':
if False: if False:
point = baseToolset.getRandomPos() point = baseToolset.getRandomPos()
print(point) print(point)
i = Isovist(*point, array=baseToolset.imgArray, walkable=walkableTiles, rangeLimit=30) i = Isovist(*point, array=baseToolset.imgArray, walkables=walkableTiles, rangeLimit=30)
i.saveImg() i.saveImg()
baseToolset.imgArray[point] = 160 baseToolset.imgArray[point] = 160
imshow(baseToolset.imgArray) imshow(baseToolset.imgArray)

View File

@ -19,7 +19,7 @@ from PCHA import PCHA
from operator import itemgetter from operator import itemgetter
from dtw import dtw from dtw import dtw
workercount = 1 workercount = 6
class Worker(object): class Worker(object):
@ -62,26 +62,26 @@ class Worker(object):
class IsovistCollection(UserDict): class IsovistCollection(UserDict):
def __init__(self, walkable, rangeLimit, tileArray, worker=None, single_threaded=False): def __init__(self, walkables, rangeLimit, tileArray, worker=None, single_threaded=False):
super(IsovistCollection, self).__init__() super(IsovistCollection, self).__init__()
if not isinstance(worker, Worker): if not isinstance(worker, Worker):
raise TypeError raise TypeError
self.data = dict() self.data = dict()
self.walkable = walkable self.walkables = walkables
self.tileArray = tileArray self.tileArray = tileArray
self.rangeLimit = rangeLimit self.rangeLimit = rangeLimit
self.lfr = None self.lfr = None
if rangeLimit: if rangeLimit:
if not single_threaded: if not single_threaded:
workerResult = worker.init_many( workerResult = worker.init_many(
Isovist, [(*npIdx, self.tileArray, self.walkable, self.rangeLimit) Isovist, [(*npIdx, self.tileArray, self.walkables, self.rangeLimit)
for npIdx, value in np.ndenumerate(self.tileArray) if value == self.walkable]) for npIdx, value in np.ndenumerate(self.tileArray) if value == self.walkables])
self.data = {isovist.vertex: isovist for isovist in workerResult} self.data = {isovist.vertex: isovist for isovist in workerResult}
# The following would be a non multithreaded approach, maybe activate it for smaller blueprints later # The following would be a non multithreaded approach, maybe activate it for smaller blueprints later
# TODO: Activate this for smaller Blueprints, when multithreading would lead to overhead # TODO: Activate this for smaller Blueprints, when multithreading would lead to overhead
else: else:
for ndIndex, value in np.ndenumerate(self.tileArray): for ndIndex, value in np.ndenumerate(self.tileArray):
if value == self.walkable or value > 0: if value in self.walkables:
self.add_isovist(*ndIndex) self.add_isovist(*ndIndex)
else: else:
pass pass
@ -99,7 +99,7 @@ class IsovistCollection(UserDict):
:rtype: bool :rtype: bool
""" """
self[(x, y)] = (Isovist(x, y, self.tileArray, self.walkable, self.rangeLimit)) self[(x, y)] = (Isovist(x, y, self.tileArray, self.walkables, self.rangeLimit))
return True return True
@staticmethod @staticmethod
@ -211,7 +211,7 @@ class IsovistCollection(UserDict):
class Isovist(object): class Isovist(object):
def __init__(self, x, y, array, walkable, rangeLimit): def __init__(self, x, y, array, walkables, rangeLimit):
""" """
"Calculate lit squares from the given location and radius through 'Shadow Casting'" "Calculate lit squares from the given location and radius through 'Shadow Casting'"
Source: Source:
@ -224,8 +224,8 @@ class Isovist(object):
:type y: int :type y: int
:param array: Numpy Array holding the background image :param array: Numpy Array holding the background image
:type array: np.ndarray :type array: np.ndarray
:param walkable: The value which identifies positions in the array through which light can travel :param walkables: The value which identifies positions in the array through which light can travel
:type walkable: int or (int, int, int) :type walkables: int or (int, int, int)
:param rangeLimit: Determine the radius in which pixels of the shadow needs to be calculated :param rangeLimit: Determine the radius in which pixels of the shadow needs to be calculated
:type rangeLimit: int :type rangeLimit: int
""" """
@ -236,14 +236,17 @@ class Isovist(object):
self.x = x self.x = x
self.y = y self.y = y
self.vertex = (self.x, self.y) self.vertex = (self.x, self.y)
self.walkables = walkables
if isinstance(array, np.ndarray): if isinstance(array, np.ndarray):
self.rangeLimit = rangeLimit if rangeLimit else int(sqrt(array.shape[0] * array.shape[1])) self.rangeLimit = rangeLimit if rangeLimit else int(sqrt(array.shape[0] * array.shape[1]))
self.radius = rangeLimit // 2 + 1
self.visArray = np.zeros(array.shape, dtype=bool) self.visArray = np.zeros(array.shape, dtype=bool)
for octant in range(8): for octant in range(8):
self.__cast_light(self.x, self.y, 1, 1.0, 0.0, self.rangeLimit, self.__cast_light(self.x, self.y, 1, 1.0, 0.0,
mult[0][octant], mult[1][octant], mult[0][octant], mult[1][octant],
mult[2][octant], mult[3][octant], 0, array, walkable) mult[2][octant], mult[3][octant], 0, array)
offset = int(rangeLimit/2) offset = int(rangeLimit/2)
# self.visArray = self.visArray[ # self.visArray = self.visArray[
@ -260,12 +263,11 @@ class Isovist(object):
self.Xcent = centroid[0] self.Xcent = centroid[0]
self.Ycent = centroid[1] self.Ycent = centroid[1]
@staticmethod def __blocksLight(self, x, y, array):
def __blocksLight(x, y, array, walkable):
if x < 0 or y < 0: if x < 0 or y < 0:
return True return True
try: try:
return False if array[x, y] == walkable or array[x, y] > 0 else True return False if array[x, y] in self.walkables else True
except IndexError: except IndexError:
return True return True
@ -280,12 +282,12 @@ class Isovist(object):
def __isVisible(self, x, y): def __isVisible(self, x, y):
return self.visArray[x, y] return self.visArray[x, y]
def __cast_light(self, cx, cy, row, start, end, radius, xx, xy, yx, yy, idx, array, walkable): def __cast_light(self, cx, cy, row, start, end, xx, xy, yx, yy, idx, array):
"""Recursive lightcasting function""" """Recursive lightcasting function from roguebasin.com"""
if start < end: if start < end:
return return
radius_squared = radius * radius radius_squared = self.radius * self.radius
for j in range(row, radius + 1): for j in range(row, self.radius + 1):
dx, dy = -j - 1, -j dx, dy = -j - 1, -j
blocked = False blocked = False
while dx <= 0: while dx <= 0:
@ -305,18 +307,17 @@ class Isovist(object):
self.__setVisible(X, Y) self.__setVisible(X, Y)
if blocked: if blocked:
# we're scanning a row of blocked squares: # we're scanning a row of blocked squares:
if self.__blocksLight(X, Y, array, walkable): if self.__blocksLight(X, Y, array):
new_start = r_slope new_start = r_slope
continue continue
else: else:
blocked = False blocked = False
start = new_start start = new_start
else: else:
if self.__blocksLight(X, Y, array, walkable) and j < radius: if self.__blocksLight(X, Y, array) and j < self.radius:
# This is a blocking square, start a child scan: # This is a blocking square, start a child scan:
blocked = True blocked = True
self.__cast_light(cx, cy, j + 1, start, l_slope, self.__cast_light(cx, cy, j + 1, start, l_slope, xx, xy, yx, yy, idx + 1, array)
radius, xx, xy, yx, yy, idx + 1, array, walkable)
new_start = r_slope new_start = r_slope
# Row is scanned; do next row unless last square was blocked: # Row is scanned; do next row unless last square was blocked:
if blocked: if blocked:
@ -381,13 +382,12 @@ class TrackCollection(UserDict):
self[key] = track self[key] = track
return True return True
def add_n_bunch_tracks(self, n, start, target, nbunch=None, penalty=None): def add_n_bunch_tracks(self, n, start, target, nbunch=None, penalty=None):
def build_track(segment1, segment2): def build_track(segment1, segment2):
combined = list() combined = list()
combined.extend(segment1 + list(reversed(segment2))) combined.extend(segment1 + list(reversed(segment2)))
return Track(combined, self.map.walkableTile) return Track(combined, self.map.walkableTiles)
if isinstance(penalty, int) or isinstance(penalty, float): if isinstance(penalty, int) or isinstance(penalty, float):
for i in range(n): for i in range(n):
@ -565,7 +565,7 @@ class TrackCollection(UserDict):
return [self[key] for key in [self.archeArray[:, 0][idx] for idx in idxArray]] return [self[key] for key in [self.archeArray[:, 0][idx] for idx in idxArray]]
def return_walkableTileList(self): def return_walkableTileList(self):
return [npIndex for npIndex, value in np.ndenumerate(self.map.imgArray) if value == self.map.walkableTile] return [npIndex for npIndex, value in np.ndenumerate(self.map.imgArray) if value in self.map.walkableTiles]
def save_to_disc(self, filename): def save_to_disc(self, filename):
filename = filename if filename.endswith('.pik') else '%s%s' % (filename, '.pik') filename = filename if filename.endswith('.pik') else '%s%s' % (filename, '.pik')
@ -699,7 +699,7 @@ class TrackCollection(UserDict):
position = c[0] if c[0] not in currentTrack else c[1] position = c[0] if c[0] not in currentTrack else c[1]
else: else:
raise ValueError('Something went wrong here, maybe no stop position?') raise ValueError('Something went wrong here, maybe no stop position?')
tracks.append(Track(currentTrack, self.map.walkableTile, qhull=False)) tracks.append(Track(currentTrack, self.map.walkableTiles, qhull=False))
self.add_n_bunch_tracks(0, 0, 0, tracks) self.add_n_bunch_tracks(0, 0, 0, tracks)
@ -711,13 +711,12 @@ class TrackCollection(UserDict):
pass pass
class Track(UserList): class Track(UserList):
def __init__(self, NodeList, walkableTile, qhull=True): def __init__(self, NodeList, walkableTiles, qhull=True):
if not isinstance(NodeList, list): if not isinstance(NodeList, list):
raise TypeError raise TypeError
super(UserList, self).__init__() super(UserList, self).__init__()
self.walkableTile = walkableTile self.walkableTiles = walkableTiles
self.data = NodeList.copy() self.data = NodeList.copy()
self.group = None self.group = None
self.vertex = None self.vertex = None
@ -799,7 +798,7 @@ class Track(UserList):
('an' if typ.startswith(prefix) else 'a', typ)) ('an' if typ.startswith(prefix) else 'a', typ))
l = self.data l = self.data
l.extend(l2) l.extend(l2)
return Track(l, self.walkableTile) return Track(l, self.walkableTiles)
def return_isovists(self, trackCollection=None, indoorToolset=None): def return_isovists(self, trackCollection=None, indoorToolset=None):
if isinstance(trackCollection, TrackCollection): if isinstance(trackCollection, TrackCollection):
@ -812,7 +811,7 @@ class Track(UserList):
class IndoorToolset(object): class IndoorToolset(object):
def __init__(self, imageArray, walkableTile, graph=None, worker=None, isoVistSize=0): def __init__(self, imageArray, walkableTiles, graph=None, worker=None, isoVistSize=0):
""" """
:param graph: An optional Graph :param graph: An optional Graph
:type graph: nx.Graph :type graph: nx.Graph
@ -820,7 +819,7 @@ class IndoorToolset(object):
if not isinstance(imageArray, np.ndarray) or not isinstance(worker, Worker): if not isinstance(imageArray, np.ndarray) or not isinstance(worker, Worker):
raise TypeError raise TypeError
self.walkableTile = walkableTile self.walkableTiles = walkableTiles
self.imgArray = imageArray self.imgArray = imageArray
self.shape = self.imgArray.shape self.shape = self.imgArray.shape
self.height = self.shape[0] self.height = self.shape[0]
@ -830,14 +829,14 @@ class IndoorToolset(object):
else: else:
self.graph = self.translate_to_graph() self.graph = self.translate_to_graph()
self.__rand = random.Random() self.__rand = random.Random()
self.isovists = IsovistCollection(self.walkableTile, isoVistSize, self.imgArray, worker=worker) self.isovists = IsovistCollection(self.walkableTiles, isoVistSize, self.imgArray,
worker=worker, single_threaded=True)
def refresh_random_clock(self): def refresh_random_clock(self):
self.__rand.seed(time.clock()) self.__rand.seed(time.clock())
def copy(self): def copy(self):
return IndoorToolset(self.imgArray.copy(), self.walkableTile, graph=self.graph.copy()) return IndoorToolset(self.imgArray.copy(), self.walkableTiles, graph=self.graph.copy())
def __len__(self): def __len__(self):
return self.width * self.height return self.width * self.height
@ -898,9 +897,11 @@ class IndoorToolset(object):
rs = self.graph.nodes()[self.__rand.randrange(len(self.graph))] rs = self.graph.nodes()[self.__rand.randrange(len(self.graph))]
if verbose: if verbose:
print(rs, ' is random Position -> checking accessibiliy') print(rs, ' is random Position -> checking accessibiliy')
notWalkable = False if self.imgArray[rs] in self.walkableTiles:
if self.imgArray[rs] != self.walkableTile:
notWalkable = True notWalkable = True
else:
notWalkable = False
while notWalkable: while notWalkable:
self.refresh_random_clock() self.refresh_random_clock()
rs = self.graph.nodes()[self.__rand.randrange(len(self.graph))] rs = self.graph.nodes()[self.__rand.randrange(len(self.graph))]
@ -913,7 +914,7 @@ class IndoorToolset(object):
def translate_to_graph(self): def translate_to_graph(self):
graph = nx.Graph() graph = nx.Graph()
for idx, value in np.ndenumerate(self.imgArray): for idx, value in np.ndenumerate(self.imgArray):
if value > 0 or value == self.walkableTile: if value in self.walkableTiles:
x, y = idx x, y = idx
graph.add_node((x, y), count=0) graph.add_node((x, y), count=0)
@ -972,7 +973,7 @@ class IndoorToolset(object):
oldWeight = self.graph.edge[currNode][nextNode]['weight'] oldWeight = self.graph.edge[currNode][nextNode]['weight']
self.graph.add_edge(currNode, nextNode, weight=oldWeight + penalty) self.graph.add_edge(currNode, nextNode, weight=oldWeight + penalty)
track = Track(dij_s_p, self.walkableTile, qhull=qhull) track = Track(dij_s_p, self.walkableTiles, qhull=qhull)
print(len(dij_s_p), ' Len Path generated -> Nodes: ', len(self.graph), ' -> Edges: ', len(self.graph.edges())) print(len(dij_s_p), ' Len Path generated -> Nodes: ', len(self.graph), ' -> Edges: ', len(self.graph.edges()))
return track return track