Procházet zdrojové kódy

add `chunked_array2d.copy()`

blueloveTH před 1 rokem
rodič
revize
3f5c460761
3 změnil soubory, kde provedl 63 přidání a 1 odebrání
  1. 6 1
      include/typings/array2d.pyi
  2. 47 0
      src/modules/array2d.c
  3. 10 0
      tests/90_chunked_array2d.py

+ 6 - 1
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."""

+ 47 - 0
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);

+ 10 - 0
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