mirror of
				https://github.com/MarioSpore/Grinch-AP.git
				synced 2025-10-21 20:21:32 -06:00 
			
		
		
		
	
		
			
	
	
		
			99 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			99 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | from typing import Callable, FrozenSet, List, Set, Tuple, Union | ||
|  | 
 | ||
|  | from .position import Point2 | ||
|  | 
 | ||
|  | 
 | ||
|  | class PixelMap: | ||
|  | 
 | ||
|  |     def __init__(self, proto, in_bits: bool = False): | ||
|  |         """
 | ||
|  |         :param proto: | ||
|  |         :param in_bits: | ||
|  |         """
 | ||
|  |         self._proto = proto | ||
|  |         # Used for copying pixelmaps | ||
|  |         self._in_bits: bool = in_bits | ||
|  | 
 | ||
|  |         assert self.width * self.height == (8 if in_bits else 1) * len( | ||
|  |             self._proto.data | ||
|  |         ), f"{self.width * self.height} {(8 if in_bits else 1)*len(self._proto.data)}" | ||
|  | 
 | ||
|  |     @property | ||
|  |     def width(self) -> int: | ||
|  |         return self._proto.size.x | ||
|  | 
 | ||
|  |     @property | ||
|  |     def height(self) -> int: | ||
|  |         return self._proto.size.y | ||
|  | 
 | ||
|  |     @property | ||
|  |     def bits_per_pixel(self) -> int: | ||
|  |         return self._proto.bits_per_pixel | ||
|  | 
 | ||
|  |     @property | ||
|  |     def bytes_per_pixel(self) -> int: | ||
|  |         return self._proto.bits_per_pixel // 8 | ||
|  | 
 | ||
|  |     def __getitem__(self, pos: Tuple[int, int]) -> int: | ||
|  |         """ Example usage: is_pathable = self._game_info.pathing_grid[Point2((20, 20))] != 0 """ | ||
|  |         assert 0 <= pos[0] < self.width, f"x is {pos[0]}, self.width is {self.width}" | ||
|  |         assert 0 <= pos[1] < self.height, f"y is {pos[1]}, self.height is {self.height}" | ||
|  |         return int(self.data_numpy[pos[1], pos[0]]) | ||
|  | 
 | ||
|  |     def __setitem__(self, pos: Tuple[int, int], value: int): | ||
|  |         """ Example usage: self._game_info.pathing_grid[Point2((20, 20))] = 255 """ | ||
|  |         assert 0 <= pos[0] < self.width, f"x is {pos[0]}, self.width is {self.width}" | ||
|  |         assert 0 <= pos[1] < self.height, f"y is {pos[1]}, self.height is {self.height}" | ||
|  |         assert ( | ||
|  |             0 <= value <= 254 * self._in_bits + 1 | ||
|  |         ), f"value is {value}, it should be between 0 and {254 * self._in_bits + 1}" | ||
|  |         assert isinstance(value, int), f"value is of type {type(value)}, it should be an integer" | ||
|  |         self.data_numpy[pos[1], pos[0]] = value | ||
|  | 
 | ||
|  |     def is_set(self, p: Tuple[int, int]) -> bool: | ||
|  |         return self[p] != 0 | ||
|  | 
 | ||
|  |     def is_empty(self, p: Tuple[int, int]) -> bool: | ||
|  |         return not self.is_set(p) | ||
|  | 
 | ||
|  |     def copy(self) -> "PixelMap": | ||
|  |         return PixelMap(self._proto, in_bits=self._in_bits) | ||
|  | 
 | ||
|  |     def flood_fill(self, start_point: Point2, pred: Callable[[int], bool]) -> Set[Point2]: | ||
|  |         nodes: Set[Point2] = set() | ||
|  |         queue: List[Point2] = [start_point] | ||
|  | 
 | ||
|  |         while queue: | ||
|  |             x, y = queue.pop() | ||
|  | 
 | ||
|  |             if not (0 <= x < self.width and 0 <= y < self.height): | ||
|  |                 continue | ||
|  | 
 | ||
|  |             if Point2((x, y)) in nodes: | ||
|  |                 continue | ||
|  | 
 | ||
|  |             if pred(self[x, y]): | ||
|  |                 nodes.add(Point2((x, y))) | ||
|  |                 queue += [Point2((x + a, y + b)) for a in [-1, 0, 1] for b in [-1, 0, 1] if not (a == 0 and b == 0)] | ||
|  |         return nodes | ||
|  | 
 | ||
|  |     def flood_fill_all(self, pred: Callable[[int], bool]) -> Set[FrozenSet[Point2]]: | ||
|  |         groups: Set[FrozenSet[Point2]] = set() | ||
|  | 
 | ||
|  |         for x in range(self.width): | ||
|  |             for y in range(self.height): | ||
|  |                 if any((x, y) in g for g in groups): | ||
|  |                     continue | ||
|  | 
 | ||
|  |                 if pred(self[x, y]): | ||
|  |                     groups.add(frozenset(self.flood_fill(Point2((x, y)), pred))) | ||
|  | 
 | ||
|  |         return groups | ||
|  | 
 | ||
|  |     def print(self, wide: bool = False) -> None: | ||
|  |         for y in range(self.height): | ||
|  |             for x in range(self.width): | ||
|  |                 print("#" if self.is_set((x, y)) else " ", end=(" " if wide else "")) | ||
|  |             print("") | ||
|  | 
 |