utilities.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. """Utility data and functions for WFC. Implementation based on https://github.com/ikarth/wfc_2019f"""
  2. from __future__ import annotations
  3. import collections
  4. import logging
  5. import numpy as np
  6. from numpy.typing import NDArray
  7. logger = logging.getLogger(__name__)
  8. CoordXY = collections.namedtuple("CoordXY", ["x", "y"])
  9. CoordRC = collections.namedtuple("CoordRC", ["row", "column"])
  10. def hash_downto(a: NDArray[np.integer], rank: int, seed=0) -> NDArray[np.int64]:
  11. state = np.random.RandomState(seed)
  12. # np_random = np.random.default_rng(seed)
  13. assert rank < len(a.shape)
  14. u: NDArray[np.integer] = a.reshape((np.prod(a.shape[:rank], dtype=np.int64), -1))
  15. v = state.randint(1 - (1 << 63), 1 << 63, np.prod(a.shape[rank:]), dtype=np.int64)
  16. # v = np_random.integers(1 - (1 << 63), 1 << 63, np.prod(a.shape[rank:]), dtype=np.int64)
  17. return np.asarray(np.inner(u, v).reshape(a.shape[:rank]), dtype=np.int64)
  18. def find_pattern_center(wfc_ns):
  19. # wfc_ns.pattern_center = (math.floor((wfc_ns.pattern_width - 1) / 2), math.floor((wfc_ns.pattern_width - 1) / 2))
  20. wfc_ns.pattern_center = (0, 0)
  21. return wfc_ns
  22. def tile_grid_to_image(
  23. tile_grid: NDArray[np.int64],
  24. tile_catalog: dict[int, NDArray[np.integer]],
  25. tile_size: tuple[int, int],
  26. partial: bool = False,
  27. color_channels: int = 3,
  28. ) -> NDArray[np.integer]:
  29. """
  30. Takes a tile_grid and transforms it into an image, using the information
  31. in tile_catalog. We use tile_size to figure out the size the new image
  32. should be.
  33. """
  34. tile_dtype = next(iter(tile_catalog.values())).dtype
  35. new_img = np.zeros(
  36. (
  37. tile_grid.shape[0] * tile_size[0],
  38. tile_grid.shape[1] * tile_size[1],
  39. color_channels,
  40. ),
  41. dtype=tile_dtype,
  42. )
  43. if partial and (len(tile_grid.shape)) > 2:
  44. # TODO: implement rendering partially completed solution
  45. # Call tile_grid_to_average() instead.
  46. assert False
  47. else:
  48. for i in range(tile_grid.shape[0]):
  49. for j in range(tile_grid.shape[1]):
  50. tile = tile_grid[i, j]
  51. for u in range(tile_size[0]):
  52. for v in range(tile_size[1]):
  53. pixel = [200, 0, 200]
  54. # If we want to display a partial pattern, it is helpful to
  55. # be able to show empty cells.
  56. pixel = tile_catalog[tile][u, v]
  57. # TODO: will need to change if using an image with more than 3 channels
  58. new_img[
  59. (i * tile_size[0]) + u, (j * tile_size[1]) + v
  60. ] = np.resize(
  61. pixel,
  62. new_img[
  63. (i * tile_size[0]) + u, (j * tile_size[1]) + v
  64. ].shape,
  65. )
  66. return new_img