diff --git a/include/typings/array2d.pyi b/include/typings/array2d.pyi index 61589735..3f5ea767 100644 --- a/include/typings/array2d.pyi +++ b/include/typings/array2d.pyi @@ -1,4 +1,4 @@ -from typing import Callable, Literal, overload, Iterator +from typing import Callable, Literal, overload, Iterator, Self from linalg import vec2i Neighborhood = Literal['Moore', 'von Neumann'] @@ -134,6 +134,10 @@ class chunked_array2d[T, TContext]: @property def chunk_size(self) -> int: ... + @property + def default(self) -> T: ... + @property + def context_builder(self) -> Callable[[vec2i], TContext] | None: ... def __getitem__(self, index: vec2i) -> T: ... def __setitem__(self, index: vec2i, value: T): ... @@ -142,6 +146,7 @@ class chunked_array2d[T, TContext]: def __len__(self) -> int: ... def clear(self) -> None: ... + def copy(self) -> Self: ... def world_to_chunk(self, world_pos: vec2i) -> tuple[vec2i, vec2i]: """Converts world position to chunk position and local position.""" diff --git a/src/modules/array2d.c b/src/modules/array2d.c index 756ff4f6..c86f367e 100644 --- a/src/modules/array2d.c +++ b/src/modules/array2d.c @@ -1027,11 +1027,26 @@ static bool chunked_array2d__new__(int argc, py_Ref argv) { } static bool chunked_array2d_chunk_size(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); c11_chunked_array2d* self = py_touserdata(argv); py_newint(py_retval(), self->chunk_size); return true; } +static bool chunked_array2d_default(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + c11_chunked_array2d* self = py_touserdata(argv); + py_assign(py_retval(), &self->default_T); + return true; +} + +static bool chunked_array2d_context_builder(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + c11_chunked_array2d* self = py_touserdata(argv); + py_assign(py_retval(), &self->context_builder); + return true; +} + static bool chunked_array2d__getitem__(int argc, py_Ref argv) { PY_CHECK_ARGC(2); PY_CHECK_ARG_TYPE(1, tp_vec2i); @@ -1089,6 +1104,7 @@ static bool chunked_array2d__len__(int argc, py_Ref argv) { } static bool chunked_array2d_clear(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); c11_chunked_array2d* self = py_touserdata(argv); c11_chunked_array2d_chunks__clear(&self->chunks); self->last_visited.value = NULL; @@ -1096,6 +1112,34 @@ static bool chunked_array2d_clear(int argc, py_Ref argv) { return true; } +static bool chunked_array2d_copy(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + c11_chunked_array2d* self = py_touserdata(argv); + c11_chunked_array2d* res = + py_newobject(py_retval(), tp_chunked_array2d, 0, sizeof(c11_chunked_array2d)); + // copy basic data + memcpy(res, self, sizeof(c11_chunked_array2d)); + // invalidate last_visited cache + self->last_visited.value = NULL; + // copy chunks + memset(&res->chunks, 0, sizeof(c11_chunked_array2d_chunks)); + c11_chunked_array2d_chunks__ctor(&res->chunks); + c11_vector__reserve(&res->chunks, self->chunks.capacity); + for(int i = 0; i < self->chunks.length; i++) { + c11_chunked_array2d_chunks_KV* kv = + c11__at(c11_chunked_array2d_chunks_KV, &self->chunks, i); + int chunk_numel = self->chunk_size * self->chunk_size + 1; + py_TValue* data = PK_MALLOC(sizeof(py_TValue) * chunk_numel); + memcpy(data, kv->value, sizeof(py_TValue) * chunk_numel); + // construct new KV + c11_chunked_array2d_chunks_KV new_kv; + new_kv.key = kv->key; + new_kv.value = data; + c11_vector__push(c11_chunked_array2d_chunks_KV, &res->chunks, new_kv); + } + return true; +} + static bool chunked_array2d_world_to_chunk(int argc, py_Ref argv) { PY_CHECK_ARGC(2); PY_CHECK_ARG_TYPE(1, tp_vec2i); @@ -1259,6 +1303,8 @@ static void register_chunked_array2d(py_Ref mod) { chunked_array2d__new__); py_bindproperty(type, "chunk_size", chunked_array2d_chunk_size, NULL); + py_bindproperty(type, "default", chunked_array2d_default, NULL); + py_bindproperty(type, "context_builder", chunked_array2d_context_builder, NULL); py_bindmagic(type, __getitem__, chunked_array2d__getitem__); py_bindmagic(type, __setitem__, chunked_array2d__setitem__); @@ -1267,6 +1313,7 @@ static void register_chunked_array2d(py_Ref mod) { py_bindmagic(type, __len__, chunked_array2d__len__); py_bindmethod(type, "clear", chunked_array2d_clear); + py_bindmethod(type, "copy", chunked_array2d_copy); py_bindmethod(type, "world_to_chunk", chunked_array2d_world_to_chunk); py_bindmethod(type, "add_chunk", chunked_array2d_add_chunk); py_bindmethod(type, "remove_chunk", chunked_array2d_remove_chunk); diff --git a/tests/90_chunked_array2d.py b/tests/90_chunked_array2d.py index 8fa56435..2e461a7e 100644 --- a/tests/90_chunked_array2d.py +++ b/tests/90_chunked_array2d.py @@ -55,3 +55,13 @@ assert a.move_chunk(vec2i(0, 1), vec2i(1, 1)) == True assert a.get_context(vec2i(1, 1)) == 1 assert a.get_context(vec2i(0, 1)) == None + +b = a.copy() +assert a is not b +assert a.chunk_size == b.chunk_size +assert a.default == b.default +assert a.context_builder == b.context_builder +assert (a.view() == b.view()).all() + +for pos, ctx in a: + assert b.get_context(pos) == ctx