Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from ipycanvas import Canvas, hold_canvas
- import numpy as np
- # A maybe overkill class to store information about a square, its boundaries, and color index.
- class Square:
- def __init__(self, U=False, L=False, D=False, R=False, row_seed=-1, col_seed=-1, color_index=0):
- # Each square has Up, Left, Down, Right boundaries.
- self.U, self.L, self.D, self.R = U, L, D, R
- # Keep the row and col generating seed for debugging.
- self.row_seed, self.col_seed = row_seed, col_seed
- # A color value of consecutive squares found via BFS.
- self.color_index = color_index
- def set_row_seed(self, row_seed):
- self.row_seed = row_seed
- def set_col_seed(self, col_seed):
- self.col_seed = col_seed
- def set_U(self, U):
- self.U = 1 if U else 0
- def set_L(self, L):
- self.L = 1 if L else 0
- def set_D(self, D):
- self.D = 1 if D else 0
- def set_R(self, R):
- self.R = 1 if R else 0
- def set_color_index(self, color_index):
- self.color_index = color_index
- def get_row_seed(self):
- return self.row_seed
- def get_col_seed(self):
- return self.col_seed
- def get_U(self):
- return 1 if self.U else 0
- def get_L(self):
- return 1 if self.L else 0
- def get_D(self):
- return 1 if self.D else 0
- def get_R(self):
- return 1 if self.R else 0
- def get_color_index(self):
- return self.color_index
- def draw_path(self, canvas, start, end):
- canvas.begin_path()
- canvas.move_to(*start)
- canvas.line_to(*end)
- canvas.stroke()
- canvas.close_path()
- def draw_cell(self, canvas, row, col, fill_shapes, color_mode=""):
- if fill_shapes:
- # Random value [0, 256^3) that adjacent squares have after BFS runs.
- c_index = self.get_color_index()
- if color_mode == "bw":
- r = c_index % 255
- g = c_index % 255
- b = c_index % 255
- else:
- # By default use colors based on the random color_index value.
- r_mask = 0xFF
- g_mask = 0xFF00
- b_mask = 0xFF0000
- # color_index is a random value max 256^3~2^24 so we can safely apply RGB masks to it.
- r = c_index & r_mask
- g = (c_index & g_mask) >> 8
- b = (c_index & b_mask) >> 16
- canvas.fill_style = "rgb({}, {}, {})".format(r, g, b)
- canvas.fill_rect(col * (W//N), row * (H//N), (W//N), (H//N))
- else:
- if self.get_U():
- start = [(col + 0) * (W//N), row * (H//N)]
- end = [(col + 1) * (W//N), row * (H//N)]
- self.draw_path(canvas, start, end)
- if self.get_L():
- start = [col * (W//N), (row + 0) * (H//N)]
- end = [col * (W//N), (row + 1) * (H//N)]
- self.draw_path(canvas, start, end)
- if self.get_D():
- start = [(col + 0) * (W//N), (row + 1) * (H//N)]
- end = [(col + 1) * (W//N), (row + 1) * (H//N)]
- self.draw_path(canvas, start, end)
- if self.get_R():
- start = [(col + 1) * (W//N), (row + 0) * (H//N)]
- end = [(col + 1) * (W//N), (row + 1) * (H//N)]
- self.draw_path(canvas, start, end)
- def __repr__(self):
- # Debug stuff hehe.
- return "{}.{}.{}.{} ({})".format(self.get_U(), self.get_L(), self.get_D(), self.get_R(), self.get_color_index())
- from collections import deque
- def BFS(matrix, square, row, col):
- queue = deque([[square, row, col]])
- color_index = np.random.randint(256**3, size=1)[0]
- while len(queue) > 0:
- cur_square, cur_row, cur_col = queue.popleft()
- cur_square.set_color_index(color_index)
- # Try to add neighbors of square to the queue unless a boundary exists between them.
- neighbors = []
- if cur_row > 0:
- square_U = matrix[cur_row - 1][cur_col]
- if square_U.get_color_index() == 0 and square_U.get_D() != 1:
- neighbors.append([square_U, cur_row - 1, cur_col])
- if cur_row + 1 < len(matrix):
- square_D = matrix[cur_row + 1][cur_col]
- if square_D.get_color_index() == 0 and square_D.get_U() != 1:
- neighbors.append([square_D, cur_row + 1, cur_col])
- if cur_col > 0:
- square_L = matrix[cur_row][cur_col - 1]
- if square_L.get_color_index() == 0 and square_L.get_R() != 1:
- neighbors.append([square_L, cur_row, cur_col - 1])
- if cur_col + 1 < len(matrix[0]):
- square_R = matrix[cur_row][cur_col + 1]
- if square_R.get_color_index() == 0 and square_R.get_L() != 1:
- neighbors.append([square_R, cur_row, cur_col + 1])
- queue = queue + deque(neighbors)
- # Width and Height of the original quadrant.
- W, H = 600, 600
- # Number of desired cells in a quadrant. Because the first quadrant will be mirrored in 4
- # directions, then the real amount of cells per axis will be twice as much this.
- N = 60
- # A matrix to store the original quadrant. The other ones will be generated by
- # mirroring this quadrant.
- matrix = [[Square() for _ in range(N)] for _ in range(N)]
- # Canvas has twice the width and height of the original quadrant to be able to fit
- # 4 versions of this quadrant but flipped around.
- canvas = Canvas(width=W*2, height=H*2, sync_image_data=True)
- canvas.scale(1)
- canvas.line_width = 1
- canvas.stroke_style = 'black'
- # Rows iterator
- for i in range(N):
- # For each row generate a random value 0 or 1.
- row_seed = np.random.randint(2, size=1)[0]
- # Now for each cell, go horizontally and set the Down boundary to True either in
- # odd or even positions.
- for j in range(N):
- square = matrix[i][j]
- square.set_row_seed(row_seed)
- if j % 2 == 0:
- square.set_D(row_seed == 0)
- else:
- square.set_D(row_seed == 1)
- # Columns iterator
- for j in range(N):
- # For each col generate a random value 0 or 1.
- col_seed = np.random.randint(2, size=1)[0]
- # Now for each cell, go vertically and set the Right boundary to True either in
- # odd or even positions.
- for i in range(N):
- square = matrix[i][j]
- square.set_col_seed(col_seed)
- if i % 2 == 0:
- square.set_R(col_seed == 0)
- else:
- square.set_R(col_seed == 1)
- for row in range(len(matrix)):
- for col in range(len(matrix[0])):
- square = matrix[row][col]
- if row > 0:
- square_U = matrix[row - 1][col]
- square.set_U(any([square.get_U(), square_U.get_D()]))
- square_U.set_D(any([square.get_U(), square_U.get_D()]))
- if row + 1 < len(matrix):
- square_D = matrix[row + 1][col]
- square.set_D(any([square.get_D(), square_D.get_U()]))
- square_D.set_U(any([square.get_D(), square_D.get_U()]))
- if col > 0:
- square_L = matrix[row][col - 1]
- square.set_L(any([square.get_L(), square_L.get_R()]))
- square_L.set_R(any([square.get_L(), square_L.get_R()]))
- if col + 1 < len(matrix[0]):
- square_R = matrix[row][col + 1]
- square.set_R(any([square.get_R(), square_R.get_L()]))
- square_R.set_L(any([square.get_R(), square_R.get_L()]))
- for row in range(len(matrix)):
- for col in range(len(matrix[0])):
- square = matrix[row][col]
- if square.get_color_index() == 0:
- BFS(matrix, square, row, col)
- # Now mirror horizontally the original matrix.
- new_matrix_1 = []
- for row in range(len(matrix)):
- old_row = matrix[row]
- new_row = []
- for col in range(len(matrix[0])):
- square = matrix[row][col]
- new_square = Square(
- # Flip the order of passing the L/R boundary values.
- square.get_U(), square.get_R(), square.get_D(), square.get_L(),
- square.get_row_seed(), square.get_col_seed(), square.get_color_index()
- )
- new_row.append(new_square)
- new_matrix_1.append(old_row + new_row[::-1])
- # Now mirror vertically the previous matrix.
- new_matrix_2 = []
- for row in range(len(new_matrix_1)):
- new_row = []
- for col in range(len(new_matrix_1[0])):
- square = new_matrix_1[row][col]
- new_square = Square(
- # Flip the order of passing the U/D boundary values.
- square.get_D(), square.get_L(), square.get_U(), square.get_R(),
- square.get_row_seed(), square.get_col_seed(), square.get_color_index()
- )
- new_row.append(new_square)
- new_matrix_2.append(new_row)
- final_matrix = new_matrix_1 + new_matrix_2[::-1]
- # All this about boundaries and squares is so that we are able to do a BFS
- # to paint them with a given color :-)
- with hold_canvas(canvas):
- # Change this before running to alternate between outlines drawing and filling shapes.
- fill_shapes = True
- for row in range(len(final_matrix)):
- for col in range(len(final_matrix[0])):
- item = final_matrix[row][col]
- item.draw_cell(canvas, row, col, fill_shapes)
- hold_canvas(canvas)
- canvas
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement