El código de ayer para dibujar los patrones (que @moebio llama ahora textiles) no era muy adecuado para entenderlos pues se concentraba, sobre todo, en dibujarlos. Una representación matricial resultaría más apropiada.
Una forma de hacerlo es imaginar que cada cuadrado de la cuadrícula puede ser de uno de cuatro tipos: o tiene una línea abajo (2), o tiene una línea a la derecha (3), o tiene ambas (2+3), o no tiene ninguna (0).
Así, dadas un par de sucesiones binarias que sirvan de semilla de filas y columnas, el textil puede ser representado por una matriz con entradas dentro de {0, 2, 3, 5}.
Esta función, que aprovecha las capacidades de numpy
, permite generarla:
import numpy as np
def generate_textil(N, row_seed, col_seed):
h = np.ones([N, N])
h[:, ::2] = (h[:, ::2].T * 2 * row_seed).T
h[:, 1::2] = (h[:, 1::2].T * 2 * (~row_seed + 2)).T
v = np.ones([N, N])
v[::2] = v[::2] * 3 * col_seed
v[1::2] = v[1::2] * 3 * (~col_seed + 2)
return h + v
Asume que row_seed
y col_seed
son arreglos de numpy
, tienen la misma
longitud, y esta es igual a N
. Me gusta que los textiles sean
cuadrados.
Por ejemplo, con semillas [0, 1, 0, 1, 0]
y [0, 0, 1, 0, 0]
(N=5
)
obtenemos:
matrix = generate_textil(
5,
np.array([0, 1, 0, 1, 0]),
np.array([0, 0, 1, 0, 0])
)
Desde ayer, @zubie7a y @moebio están coloreando textiles (1 y 2) y aquí el primero compartió su código. Con sus ideas adaptadas a mi codificación, la clave de la coloreada resulta ser un algoritmo BFS (Breadth-first search) que permita reconocer regiones contiguas. Esencialmente, para cada punto de la matriz que no tenga una región asignada aún se asigna una región y después se recorre el textil buscando otros miembros de la región utilizando las barreras como límite de la búsqueda.
Esto en código se ve así:
def determine_regions(matrix):
region_matrix = np.zeros_like(matrix)
region_index = 1
it = np.nditer(region_matrix, flags=['multi_index'])
with it:
while not it.finished:
row, col = it.multi_index
if it[0] == 0:
BFS(matrix, row, col, region_matrix, region_index)
region_index = region_index + 1
it.iternext()
return region_matrix
Con BFS
definido así:
from collections import deque
def BFS(matrix, row, col, region_matrix, region_index):
queue = deque([(row, col)])
while len(queue) > 0:
current = queue.popleft()
if region_matrix[current] != 0:
continue
region_matrix[current] = region_index
# Try to add neighbors of square to the queue unless a boundary exists between them.
neighbors = []
if current[0] > 0:
above = (current[0] - 1, current[1])
if matrix[above] == 0 or matrix[above] == 3:
neighbors.append(above)
if current[0] + 1 < matrix.shape[0]:
below = (current[0] + 1, current[1])
if matrix[current] == 0 or matrix[current] == 3:
neighbors.append(below)
if current[1] > 0:
left = (current[0], current[1] - 1)
if matrix[left] == 0 or matrix[left] == 2:
neighbors.append(left)
if current[1] + 1 < matrix.shape[1]:
right = (current[0], current[1] + 1)
if matrix[current] == 0 or matrix[current] == 2:
neighbors.append(right)
queue = queue + deque([x for x in neighbors if region_matrix[x] == 0])
Con este truco, podemos colorear el textil de arriba fácilmente usando, por ejemplo:
np.random.seed(335)
region_matrix = determine_regions(matrix)
colors = [(np.random.randint(255, size=3)/256.0).tolist() + [1.]
for _ in range(int(region_matrix.max()))]
cmap_color = matplotlib.colors.ListedColormap(colors, name='colors', N=None)
fig = plt.figure(figsize = (10,10))
ax = fig.add_subplot(111)
ax.imshow(region_matrix, cmap=cmap_color)
plt.axis('off')
plt.show()
Aquí un textil de 80x80 generado con semillas al azar:
N = 80
np.random.seed(335)
a = np.random.binomial(1, 0.5, N)
b = np.random.binomial(1, 0.5, N)
matrix = generate_textil(N, a, b)
Tiene 526 regiones.
Y ahora uno de 1000x1000 (70514 regiones):