This commit is contained in:
blueloveTH 2024-11-25 12:42:42 +08:00
parent b053fe32a8
commit 1ca69b3a35
3 changed files with 42 additions and 0 deletions

View File

@ -84,3 +84,10 @@ class array2d(Generic[T]):
def convolve(self: array2d[int], kernel: array2d[int], padding: int) -> array2d[int]: def convolve(self: array2d[int], kernel: array2d[int], padding: int) -> array2d[int]:
"""Convolves the array with the given kernel.""" """Convolves the array with the given kernel."""
def get_connected_components(self, value: T, neighborhood: Neighborhood) -> tuple[array2d[int], int]:
"""Gets connected components of the grid.
Returns the `visited` array and the number of connected components,
where `0` means unvisited, and non-zero means the index of the connected component.
"""

View File

@ -764,6 +764,14 @@ void pk__add_module_array2d() {
py_bindmethod(array2d, "get_bounding_rect", array2d_get_bounding_rect); py_bindmethod(array2d, "get_bounding_rect", array2d_get_bounding_rect);
py_bindmethod(array2d, "count_neighbors", array2d_count_neighbors); py_bindmethod(array2d, "count_neighbors", array2d_count_neighbors);
py_bindmethod(array2d, "convolve", array2d_convolve); py_bindmethod(array2d, "convolve", array2d_convolve);
const char* scc =
"\ndef get_connected_components(self, value: T, neighborhood: Neighborhood) -> tuple[array2d[int], int]:\n from collections import deque\n from linalg import vec2i\n\n DIRS = [vec2i.LEFT, vec2i.RIGHT, vec2i.UP, vec2i.DOWN]\n assert neighborhood in ['Moore', 'von Neumann']\n\n if neighborhood == 'Moore':\n DIRS.extend([\n vec2i.LEFT+vec2i.UP,\n vec2i.RIGHT+vec2i.UP,\n vec2i.LEFT+vec2i.DOWN,\n vec2i.RIGHT+vec2i.DOWN\n ])\n\n visited = array2d[int](self.width, self.height, default=0)\n queue = deque()\n count = 0\n for y in range(self.height):\n for x in range(self.width):\n if visited[x, y] or self[x, y] != value:\n continue\n count += 1\n queue.append((x, y))\n visited[x, y] = count\n while queue:\n cx, cy = queue.popleft()\n for dx, dy in DIRS:\n nx, ny = cx+dx, cy+dy\n if self.is_valid(nx, ny) and not visited[nx, ny] and self[nx, ny] == value:\n queue.append((nx, ny))\n visited[nx, ny] = count\n return visited, count\n\narray2d.get_connected_components = get_connected_components\ndel get_connected_components\n";
if(!py_exec(scc, "array2d.py", EXEC_MODE, mod)) {
py_printexc();
c11__abort("failed to execute array2d.py");
}
} }
#undef INC_COUNT #undef INC_COUNT

View File

@ -217,6 +217,33 @@ assert res[mask] == [0, 4, 5, 0, 4, 5]
res[mask] = -1 res[mask] = -1
assert res.tolist() == [[-1, -1, 9, 9, -1], [-1, -1, 9, 9, -1]] assert res.tolist() == [[-1, -1, 9, 9, -1], [-1, -1, 9, 9, -1]]
# test get_connected_components
a = array2d[int].fromlist([
[1, 1, 0, 1],
[0, 2, 2, 1],
[0, 1, 1, 1],
[1, 0, 0, 0],
])
vis, cnt = a.get_connected_components(1, 'von Neumann')
assert vis == [
[1, 1, 0, 2],
[0, 0, 0, 2],
[0, 2, 2, 2],
[3, 0, 0, 0]
]
assert cnt == 3
vis, cnt = a.get_connected_components(1, 'Moore')
assert vis == [
[1, 1, 0, 2],
[0, 0, 0, 2],
[0, 2, 2, 2],
[2, 0, 0, 0]
]
assert cnt == 2
vis, cnt = a.get_connected_components(2, 'von Neumann')
assert cnt == 1
vis, cnt = a.get_connected_components(0, 'Moore')
assert cnt == 2
# stackoverflow bug due to recursive mark-and-sweep # stackoverflow bug due to recursive mark-and-sweep
# class Cell: # class Cell: