noche

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])
)

textil 01010-00100

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()

textil 01010-00100 color

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)

textil 80x80 color

Tiene 526 regiones.

Y ahora uno de 1000x1000 (70514 regiones):

textil 1000x1000 color