Source code for dye_blot

import numpy as np

[docs]class DyeBlot: def __init__(self, grid_dims, cell_dims): self._grid_dims = grid_dims """Width and height of the grid in a 2-element list.""" self._cell_dims = cell_dims """Width and height of the cells in a 2-element list.""" self._cells = [] """A list of 2-tuples containing cell coordinates."""
[docs] def num_cells(self): """Returns the number of cells in the blot.""" return len(self._cells)
[docs] def for_each_cell(self, function): """ Calls function(x, y) for each cell in the blot. Args: function: A callable object. """ for cell in self._cells: function(cell[0], cell[1])
[docs]def make_blot(grid_dims, cell_dims, req_num_cells, permitted=None, squeeze=False): """ Create a DyeBlot on a given grid, perhaps enforcing a mask. If permitted is specified, only cells for which permitted has a truthy value will be selected. If squeeze is True, non-permitted cells will simply be removed from consideration when extending a blot; if it is False, blot construction will be stopped and None returned if a non-permitted cell is used. Args: grid_dims (List): A 2-element list with width and height \ of the grid, in that order. cell_dims (List): A 2-element list with width and height \ off the cells, in that order. req_num_cells (Int): The required size of the blot. permitted (np.ndarray): A 2D array of size corresponding to \ grid_dims. Returns: (Union[DyeBlot, None]) The constructed DyeBlot, or None if \ squeeze is False and a masked cell was used. """ from numpy.random import random from numpy.random import choice grid_width = grid_dims[0] grid_height = grid_dims[1] cell_width = cell_dims[0] cell_height = cell_dims[1] blot = DyeBlot(grid_dims, cell_dims) if permitted is None: permitted = np.ones(grid_dims, dtype=bool) neighbours = {} # dict of (x, y) -> shared_edge_length def _add_neighbour(neighbour, new_edge_length): if neighbour in blot._cells: return if neighbour in neighbours: neighbours[neighbour] += new_edge_length else: neighbours[neighbour] = new_edge_length def _add_cell(cell): blot._cells.append(cell) _add_neighbour((cell[0] - 1, cell[1]), cell_height) _add_neighbour((cell[0], cell[1] - 1), cell_width) _add_neighbour((cell[0] + 1, cell[1]), cell_height) _add_neighbour((cell[0], cell[1] + 1), cell_width) def _draw_neighbour(): cells, lengths = zip(*neighbours.items()) indices = np.arange(0, len(cells)) probabilities = np.asarray(lengths) / float(sum(lengths)) index = choice(indices, p=probabilities) return cells[index] def _is_permitted(neighbour): if neighbour[0] < 0: return False if neighbour[0] >= grid_width: return False if neighbour[1] < 0: return False if neighbour[1] >= grid_height: return False return permitted[neighbour] cx = int(np.floor(random() * grid_width)) cy = int(np.floor(random() * grid_height)) start = cx, cy if _is_permitted(start): _add_cell(start) while neighbours != {} and len(blot._cells) < req_num_cells: neighbour = _draw_neighbour() if _is_permitted(neighbour): _add_cell(neighbour) elif not squeeze: return None del(neighbours[neighbour]) else: return None return blot
if __name__ == '__main__': blot = make_blot([1200, 1600], [30, 30], 10) print(blot)