amulet-core 1.9.19__py3-none-any.whl → 1.9.20__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of amulet-core might be problematic. Click here for more details.
- amulet/__init__.py +27 -27
- amulet/__pyinstaller/__init__.py +2 -2
- amulet/__pyinstaller/hook-amulet.py +4 -4
- amulet/_version.py +21 -21
- amulet/api/__init__.py +2 -2
- amulet/api/abstract_base_entity.py +128 -128
- amulet/api/block.py +630 -630
- amulet/api/block_entity.py +71 -71
- amulet/api/cache.py +107 -107
- amulet/api/chunk/__init__.py +6 -6
- amulet/api/chunk/biomes.py +207 -207
- amulet/api/chunk/block_entity_dict.py +175 -175
- amulet/api/chunk/blocks.py +46 -46
- amulet/api/chunk/chunk.py +389 -389
- amulet/api/chunk/entity_list.py +75 -75
- amulet/api/chunk/status.py +167 -167
- amulet/api/data_types/__init__.py +4 -4
- amulet/api/data_types/generic_types.py +4 -4
- amulet/api/data_types/operation_types.py +16 -16
- amulet/api/data_types/world_types.py +49 -49
- amulet/api/data_types/wrapper_types.py +71 -71
- amulet/api/entity.py +74 -74
- amulet/api/errors.py +119 -119
- amulet/api/history/__init__.py +36 -36
- amulet/api/history/base/__init__.py +3 -3
- amulet/api/history/base/base_history.py +26 -26
- amulet/api/history/base/history_manager.py +63 -63
- amulet/api/history/base/revision_manager.py +73 -73
- amulet/api/history/changeable.py +15 -15
- amulet/api/history/data_types.py +7 -7
- amulet/api/history/history_manager/__init__.py +3 -3
- amulet/api/history/history_manager/container.py +102 -102
- amulet/api/history/history_manager/database.py +279 -279
- amulet/api/history/history_manager/meta.py +93 -93
- amulet/api/history/history_manager/object.py +116 -116
- amulet/api/history/revision_manager/__init__.py +2 -2
- amulet/api/history/revision_manager/disk.py +33 -33
- amulet/api/history/revision_manager/ram.py +12 -12
- amulet/api/item.py +75 -75
- amulet/api/level/__init__.py +4 -4
- amulet/api/level/base_level/__init__.py +1 -1
- amulet/api/level/base_level/base_level.py +1035 -1026
- amulet/api/level/base_level/chunk_manager.py +227 -227
- amulet/api/level/base_level/clone.py +389 -389
- amulet/api/level/base_level/player_manager.py +101 -101
- amulet/api/level/immutable_structure/__init__.py +1 -1
- amulet/api/level/immutable_structure/immutable_structure.py +94 -94
- amulet/api/level/immutable_structure/void_format_wrapper.py +117 -117
- amulet/api/level/structure.py +22 -22
- amulet/api/level/world.py +19 -19
- amulet/api/partial_3d_array/__init__.py +2 -2
- amulet/api/partial_3d_array/base_partial_3d_array.py +263 -263
- amulet/api/partial_3d_array/bounded_partial_3d_array.py +528 -528
- amulet/api/partial_3d_array/data_types.py +15 -15
- amulet/api/partial_3d_array/unbounded_partial_3d_array.py +229 -229
- amulet/api/partial_3d_array/util.py +152 -152
- amulet/api/player.py +65 -65
- amulet/api/registry/__init__.py +2 -2
- amulet/api/registry/base_registry.py +34 -34
- amulet/api/registry/biome_manager.py +153 -153
- amulet/api/registry/block_manager.py +156 -156
- amulet/api/selection/__init__.py +2 -2
- amulet/api/selection/abstract_selection.py +315 -315
- amulet/api/selection/box.py +805 -805
- amulet/api/selection/group.py +488 -488
- amulet/api/structure.py +37 -37
- amulet/api/wrapper/__init__.py +8 -8
- amulet/api/wrapper/chunk/interface.py +441 -441
- amulet/api/wrapper/chunk/translator.py +567 -567
- amulet/api/wrapper/format_wrapper.py +772 -772
- amulet/api/wrapper/structure_format_wrapper.py +116 -116
- amulet/api/wrapper/world_format_wrapper.py +63 -63
- amulet/level/__init__.py +1 -1
- amulet/level/formats/anvil_forge_world.py +40 -40
- amulet/level/formats/anvil_world/__init__.py +3 -3
- amulet/level/formats/anvil_world/_sector_manager.py +291 -384
- amulet/level/formats/anvil_world/data_pack/__init__.py +2 -2
- amulet/level/formats/anvil_world/data_pack/data_pack.py +224 -224
- amulet/level/formats/anvil_world/data_pack/data_pack_manager.py +77 -77
- amulet/level/formats/anvil_world/dimension.py +177 -177
- amulet/level/formats/anvil_world/format.py +769 -769
- amulet/level/formats/anvil_world/region.py +384 -384
- amulet/level/formats/construction/__init__.py +3 -3
- amulet/level/formats/construction/format_wrapper.py +515 -515
- amulet/level/formats/construction/interface.py +134 -134
- amulet/level/formats/construction/section.py +60 -60
- amulet/level/formats/construction/util.py +165 -165
- amulet/level/formats/leveldb_world/__init__.py +3 -3
- amulet/level/formats/leveldb_world/chunk.py +33 -33
- amulet/level/formats/leveldb_world/dimension.py +385 -419
- amulet/level/formats/leveldb_world/format.py +659 -641
- amulet/level/formats/leveldb_world/interface/chunk/__init__.py +36 -36
- amulet/level/formats/leveldb_world/interface/chunk/base_leveldb_interface.py +836 -836
- amulet/level/formats/leveldb_world/interface/chunk/generate_interface.py +31 -31
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_0.py +30 -30
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_1.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_10.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_11.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_12.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_13.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_14.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_15.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_16.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_17.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_18.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_19.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_2.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_20.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_21.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_22.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_23.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_24.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_25.py +24 -24
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_26.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_27.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_28.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_29.py +33 -33
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_3.py +57 -57
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_30.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_31.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_32.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_33.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_34.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_35.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_36.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_37.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_38.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_39.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_4.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_40.py +16 -16
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_5.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_6.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_7.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_8.py +180 -180
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_9.py +18 -18
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_chunk_versions.py +79 -79
- amulet/level/formats/mcstructure/__init__.py +3 -3
- amulet/level/formats/mcstructure/chunk.py +50 -50
- amulet/level/formats/mcstructure/format_wrapper.py +408 -408
- amulet/level/formats/mcstructure/interface.py +175 -175
- amulet/level/formats/schematic/__init__.py +3 -3
- amulet/level/formats/schematic/chunk.py +55 -55
- amulet/level/formats/schematic/data_types.py +4 -4
- amulet/level/formats/schematic/format_wrapper.py +373 -373
- amulet/level/formats/schematic/interface.py +142 -142
- amulet/level/formats/sponge_schem/__init__.py +4 -4
- amulet/level/formats/sponge_schem/chunk.py +62 -62
- amulet/level/formats/sponge_schem/format_wrapper.py +463 -463
- amulet/level/formats/sponge_schem/interface.py +118 -118
- amulet/level/formats/sponge_schem/varint/__init__.py +1 -1
- amulet/level/formats/sponge_schem/varint/varint.py +87 -87
- amulet/level/interfaces/chunk/anvil/anvil_0.py +72 -72
- amulet/level/interfaces/chunk/anvil/anvil_1444.py +336 -336
- amulet/level/interfaces/chunk/anvil/anvil_1466.py +94 -94
- amulet/level/interfaces/chunk/anvil/anvil_1467.py +37 -37
- amulet/level/interfaces/chunk/anvil/anvil_1484.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1503.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1519.py +34 -34
- amulet/level/interfaces/chunk/anvil/anvil_1901.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1908.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1912.py +21 -21
- amulet/level/interfaces/chunk/anvil/anvil_1934.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_2203.py +69 -69
- amulet/level/interfaces/chunk/anvil/anvil_2529.py +19 -19
- amulet/level/interfaces/chunk/anvil/anvil_2681.py +76 -76
- amulet/level/interfaces/chunk/anvil/anvil_2709.py +19 -19
- amulet/level/interfaces/chunk/anvil/anvil_2844.py +267 -267
- amulet/level/interfaces/chunk/anvil/anvil_3463.py +19 -19
- amulet/level/interfaces/chunk/anvil/anvil_na.py +607 -607
- amulet/level/interfaces/chunk/anvil/base_anvil_interface.py +326 -326
- amulet/level/load.py +59 -59
- amulet/level/loader.py +95 -95
- amulet/level/translators/chunk/bedrock/__init__.py +267 -267
- amulet/level/translators/chunk/bedrock/bedrock_nbt_blockstate_translator.py +46 -46
- amulet/level/translators/chunk/bedrock/bedrock_numerical_translator.py +39 -39
- amulet/level/translators/chunk/bedrock/bedrock_psudo_numerical_translator.py +37 -37
- amulet/level/translators/chunk/java/java_1_18_translator.py +40 -40
- amulet/level/translators/chunk/java/java_blockstate_translator.py +94 -94
- amulet/level/translators/chunk/java/java_numerical_translator.py +62 -62
- amulet/libs/leveldb/__init__.py +7 -7
- amulet/operations/__init__.py +5 -5
- amulet/operations/clone.py +18 -18
- amulet/operations/delete_chunk.py +32 -32
- amulet/operations/fill.py +30 -30
- amulet/operations/paste.py +65 -65
- amulet/operations/replace.py +58 -58
- amulet/utils/__init__.py +14 -14
- amulet/utils/format_utils.py +41 -41
- amulet/utils/generator.py +15 -15
- amulet/utils/matrix.py +243 -243
- amulet/utils/numpy_helpers.py +46 -46
- amulet/utils/world_utils.py +349 -349
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/METADATA +97 -97
- amulet_core-1.9.20.dist-info/RECORD +208 -0
- amulet_core-1.9.19.dist-info/RECORD +0 -208
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/WHEEL +0 -0
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/entry_points.txt +0 -0
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/top_level.txt +0 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
from typing import Union, Tuple, Type
|
|
2
|
-
import numpy
|
|
3
|
-
|
|
4
|
-
IntSlicesType = Tuple[int, int, int]
|
|
5
|
-
UnpackedSliceType = Tuple[int, int, int]
|
|
6
|
-
UnpackedSlicesType = Tuple[UnpackedSliceType, UnpackedSliceType, UnpackedSliceType]
|
|
7
|
-
SliceSlicesType = Tuple[slice, slice, slice]
|
|
8
|
-
SingleFlexibleSliceType = Union[int, numpy.integer, slice]
|
|
9
|
-
FlexibleSlicesType = Tuple[
|
|
10
|
-
SingleFlexibleSliceType, SingleFlexibleSliceType, SingleFlexibleSliceType
|
|
11
|
-
]
|
|
12
|
-
DtypeType = Union[Type[numpy.dtype], Type[bool]]
|
|
13
|
-
|
|
14
|
-
Integer = (int, numpy.integer)
|
|
15
|
-
IntegerType = Union[int, numpy.integer]
|
|
1
|
+
from typing import Union, Tuple, Type
|
|
2
|
+
import numpy
|
|
3
|
+
|
|
4
|
+
IntSlicesType = Tuple[int, int, int]
|
|
5
|
+
UnpackedSliceType = Tuple[int, int, int]
|
|
6
|
+
UnpackedSlicesType = Tuple[UnpackedSliceType, UnpackedSliceType, UnpackedSliceType]
|
|
7
|
+
SliceSlicesType = Tuple[slice, slice, slice]
|
|
8
|
+
SingleFlexibleSliceType = Union[int, numpy.integer, slice]
|
|
9
|
+
FlexibleSlicesType = Tuple[
|
|
10
|
+
SingleFlexibleSliceType, SingleFlexibleSliceType, SingleFlexibleSliceType
|
|
11
|
+
]
|
|
12
|
+
DtypeType = Union[Type[numpy.dtype], Type[bool]]
|
|
13
|
+
|
|
14
|
+
Integer = (int, numpy.integer)
|
|
15
|
+
IntegerType = Union[int, numpy.integer]
|
|
@@ -1,229 +1,229 @@
|
|
|
1
|
-
from typing import Union, Tuple, overload, Iterable, Optional, Dict # , Literal
|
|
2
|
-
import numpy
|
|
3
|
-
import math
|
|
4
|
-
|
|
5
|
-
from .base_partial_3d_array import BasePartial3DArray
|
|
6
|
-
from .util import sanitise_slice, to_slice, unpack_slice, sanitise_unbounded_slice
|
|
7
|
-
from .data_types import DtypeType, Integer, IntegerType
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class UnboundedPartial3DArray(BasePartial3DArray):
|
|
11
|
-
"""
|
|
12
|
-
This is designed to work similarly to a numpy.ndarray but stores the data in a very different way.
|
|
13
|
-
A numpy.ndarray stores a fixed size continuous array which for large arrays can become unmanageable.
|
|
14
|
-
Sparse arrays allow individual values to exist which can be great where a small set of values are
|
|
15
|
-
defined in a large area but get less efficient the denser the defined values are.
|
|
16
|
-
|
|
17
|
-
This class was born out of the need for an array that has a fixed size in the horizontal directions but an
|
|
18
|
-
unlimited height in the vertical distance (both above and below the origin)
|
|
19
|
-
This is achieved by splitting the array into sections of fixed height and storing them sparsely so that only
|
|
20
|
-
the defined sections need to be held in memory.
|
|
21
|
-
|
|
22
|
-
This class implements an API that resembles that of a numpy array but it is not directly compatible with numpy.
|
|
23
|
-
This class also implements methods to access and directly modify the underlying numpy arrays to give finer control.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
def __init__(
|
|
27
|
-
self,
|
|
28
|
-
dtype: DtypeType,
|
|
29
|
-
default_value: Union[int, bool],
|
|
30
|
-
section_shape: Tuple[int, int, int],
|
|
31
|
-
default_section_counts: Tuple[int, int],
|
|
32
|
-
sections: Optional[Dict[int, numpy.ndarray]] = None,
|
|
33
|
-
):
|
|
34
|
-
"""
|
|
35
|
-
Construct a :class:`UnboundedPartial3DArray`. This should not be used directly. You should use the relevant subclass for your use.
|
|
36
|
-
|
|
37
|
-
:param dtype: The dtype that all arrays will be stored in.
|
|
38
|
-
:param default_value: The default value that all undefined arrays will be populated with if required.
|
|
39
|
-
:param section_shape: The shape of each section array.
|
|
40
|
-
:param default_section_counts: A tuple containing the default number of sections above and below the origin in the y axis. This is used to define the default bounds.
|
|
41
|
-
:param sections: The sections to initialise the array with.
|
|
42
|
-
"""
|
|
43
|
-
super().__init__(
|
|
44
|
-
dtype,
|
|
45
|
-
default_value,
|
|
46
|
-
section_shape,
|
|
47
|
-
(0, None, 0),
|
|
48
|
-
(section_shape[0], None, section_shape[0]),
|
|
49
|
-
(1, 1, 1),
|
|
50
|
-
sections=sections,
|
|
51
|
-
)
|
|
52
|
-
self._default_min_y = -default_section_counts[0] * self.section_shape[1]
|
|
53
|
-
self._default_max_y = default_section_counts[1] * self.section_shape[1]
|
|
54
|
-
|
|
55
|
-
def __repr__(self):
|
|
56
|
-
return f"UnboundedPartial3DArray(dtype={self.dtype}, shape={self.shape})"
|
|
57
|
-
|
|
58
|
-
@property
|
|
59
|
-
def size_y(self) -> float: # Literal[math.inf]:
|
|
60
|
-
"""The size of the array in the y axis. Is always :attr:`math.inf` for the unbounded variant. Read Only"""
|
|
61
|
-
return math.inf
|
|
62
|
-
|
|
63
|
-
@property
|
|
64
|
-
def sections(self) -> Iterable[int]:
|
|
65
|
-
"""An iterable of the section defined in the array."""
|
|
66
|
-
return self._sections.keys()
|
|
67
|
-
|
|
68
|
-
def create_section(self, sy: IntegerType):
|
|
69
|
-
"""
|
|
70
|
-
Create a section array at the given location using the default value and dtype.
|
|
71
|
-
|
|
72
|
-
:param sy: The section index to create.
|
|
73
|
-
"""
|
|
74
|
-
self._sections[int(sy)] = numpy.full(
|
|
75
|
-
self.section_shape, self.default_value, dtype=self._dtype
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
def has_section(self, sy: int) -> bool:
|
|
79
|
-
"""Check if the array for a given section exists.
|
|
80
|
-
:param cy: The section y index
|
|
81
|
-
:return: True if the array exists, False otherwise
|
|
82
|
-
"""
|
|
83
|
-
return sy in self._sections
|
|
84
|
-
|
|
85
|
-
def add_section(self, sy: IntegerType, section: numpy.ndarray):
|
|
86
|
-
"""
|
|
87
|
-
Add a section array at the given location.
|
|
88
|
-
|
|
89
|
-
:param sy: The section index to assign the array to.
|
|
90
|
-
:param section: The array to assign to the section. The shape must equal :attr:`section_shape`.
|
|
91
|
-
"""
|
|
92
|
-
if section.shape != self._section_shape:
|
|
93
|
-
raise ValueError(
|
|
94
|
-
f"The size of all sections must be equal to the section_shape. Expected shape {self._section_shape}, got {section.shape}"
|
|
95
|
-
)
|
|
96
|
-
if section.dtype != self._dtype:
|
|
97
|
-
section = section.astype(self._dtype)
|
|
98
|
-
self._sections[int(sy)] = section
|
|
99
|
-
|
|
100
|
-
def get_section(self, sy: Union[int, numpy.integer]) -> numpy.ndarray:
|
|
101
|
-
"""
|
|
102
|
-
Get the section array for a given section index.
|
|
103
|
-
|
|
104
|
-
If the section is not defined it will be populated using :meth:`create_section`
|
|
105
|
-
|
|
106
|
-
:param sy: The section index to get.
|
|
107
|
-
:return: Numpy array for this section
|
|
108
|
-
"""
|
|
109
|
-
if sy not in self._sections:
|
|
110
|
-
self.create_section(int(sy))
|
|
111
|
-
return self._sections[sy]
|
|
112
|
-
|
|
113
|
-
def __setitem__(
|
|
114
|
-
self,
|
|
115
|
-
slices: Tuple[
|
|
116
|
-
Union[IntegerType, slice],
|
|
117
|
-
Union[IntegerType, slice],
|
|
118
|
-
Union[IntegerType, slice],
|
|
119
|
-
],
|
|
120
|
-
value: Union[int, numpy.integer, numpy.ndarray],
|
|
121
|
-
):
|
|
122
|
-
"""
|
|
123
|
-
Set a sub-section of the infinite height array.
|
|
124
|
-
|
|
125
|
-
>>> # set the value at a given location
|
|
126
|
-
>>> partial_array[3, 4, 5] = 1
|
|
127
|
-
>>> # set a cuboid volume in the array
|
|
128
|
-
>>> partial_array[2:3, 4:5, 6:7] = 1
|
|
129
|
-
>>> # slice and int can be mixed
|
|
130
|
-
>>> partial_array[2:3, 4, 6:7] = 1
|
|
131
|
-
>>> # if an unbounded slice is given in the y axis it will be capped at the default max and min y values.
|
|
132
|
-
>>> partial_array[2:3, :, 6:7] = 1
|
|
133
|
-
|
|
134
|
-
:param slices: The slices or locations that define the volume to set.
|
|
135
|
-
:param value: The value to set at the given location. Can be an integer or array.
|
|
136
|
-
"""
|
|
137
|
-
if isinstance(value, Integer) and all(isinstance(s, Integer) for s in slices):
|
|
138
|
-
sy, dy = self._section_index(slices[1])
|
|
139
|
-
self.get_section(sy)[(slices[0], dy, slices[2])] = value
|
|
140
|
-
else:
|
|
141
|
-
self[slices][:, :, :] = value
|
|
142
|
-
|
|
143
|
-
@overload
|
|
144
|
-
def __getitem__(self, slices: Tuple[IntegerType, IntegerType, IntegerType]) -> int:
|
|
145
|
-
...
|
|
146
|
-
|
|
147
|
-
@overload
|
|
148
|
-
def __getitem__(
|
|
149
|
-
self,
|
|
150
|
-
slices: Tuple[
|
|
151
|
-
Union[IntegerType, slice],
|
|
152
|
-
Union[IntegerType, slice],
|
|
153
|
-
Union[IntegerType, slice],
|
|
154
|
-
],
|
|
155
|
-
) -> "BoundedPartial3DArray":
|
|
156
|
-
...
|
|
157
|
-
|
|
158
|
-
def __getitem__(self, item):
|
|
159
|
-
"""
|
|
160
|
-
Get a value or sub-section of the unbounded array.
|
|
161
|
-
|
|
162
|
-
>>> # get the value at a given location
|
|
163
|
-
>>> value = partial_array[3, 4, 5] # an integer
|
|
164
|
-
>>> # get a cuboid volume in the array
|
|
165
|
-
>>> value = partial_array[2:3, 4:5, 6:7] # BoundedPartial3DArray
|
|
166
|
-
>>> # slice and int can be mixed
|
|
167
|
-
>>> value = partial_array[2:3, 4, 6:7] # BoundedPartial3DArray
|
|
168
|
-
>>> # if an unbounded slice is given in the y axis it will be capped at the default max and min y values.
|
|
169
|
-
>>> value = partial_array[2:3, :, 6:7] # BoundedPartial3DArray
|
|
170
|
-
|
|
171
|
-
:param item: The slices to extract.
|
|
172
|
-
:return: The value or BoundedPartial3DArray viewing into this array.
|
|
173
|
-
"""
|
|
174
|
-
if isinstance(item, tuple):
|
|
175
|
-
if len(item) != 3:
|
|
176
|
-
raise KeyError(f"Tuple item must be of length 3, got {len(item)}")
|
|
177
|
-
if all(isinstance(i, Integer) for i in item):
|
|
178
|
-
sy = item[1] // self.section_shape[1]
|
|
179
|
-
if sy in self:
|
|
180
|
-
return int(
|
|
181
|
-
self._sections[sy][
|
|
182
|
-
(item[0], item[1] % self.section_shape[1], item[2])
|
|
183
|
-
]
|
|
184
|
-
)
|
|
185
|
-
else:
|
|
186
|
-
return self.default_value
|
|
187
|
-
|
|
188
|
-
elif all(isinstance(i, (int, numpy.integer, slice)) for i in item):
|
|
189
|
-
start_x, stop_x, step_x = sanitise_slice(
|
|
190
|
-
*unpack_slice(to_slice(item[0])), self.size_x
|
|
191
|
-
)
|
|
192
|
-
start_y, stop_y, step_y = sanitise_unbounded_slice(
|
|
193
|
-
*unpack_slice(to_slice(item[1])),
|
|
194
|
-
self._default_min_y,
|
|
195
|
-
self._default_max_y,
|
|
196
|
-
)
|
|
197
|
-
start_z, stop_z, step_z = sanitise_slice(
|
|
198
|
-
*unpack_slice(to_slice(item[2])), self.size_z
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
return BoundedPartial3DArray.from_partial_array(
|
|
202
|
-
self,
|
|
203
|
-
(start_x, start_y, start_z),
|
|
204
|
-
(stop_x, stop_y, stop_z),
|
|
205
|
-
(step_x, step_y, step_z),
|
|
206
|
-
)
|
|
207
|
-
else:
|
|
208
|
-
raise KeyError(f"Unsupported tuple {item} for getitem")
|
|
209
|
-
|
|
210
|
-
else:
|
|
211
|
-
raise KeyError(
|
|
212
|
-
f"{item.__class__.__name__}({item}) is not a supported input for __getitem__"
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
def __array__(self, dtype=None):
|
|
216
|
-
"""
|
|
217
|
-
Get the data contained within as a numpy array.
|
|
218
|
-
|
|
219
|
-
The y axis will be clamped to the default minimum and maximum y values.
|
|
220
|
-
|
|
221
|
-
>>> numpy.array(partial_array)
|
|
222
|
-
|
|
223
|
-
:param dtype: The dtype of the returned numpy array.
|
|
224
|
-
:return: A numpy array of the contained data.
|
|
225
|
-
"""
|
|
226
|
-
return self[:, :, :].__array__(dtype)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
from .bounded_partial_3d_array import BoundedPartial3DArray
|
|
1
|
+
from typing import Union, Tuple, overload, Iterable, Optional, Dict # , Literal
|
|
2
|
+
import numpy
|
|
3
|
+
import math
|
|
4
|
+
|
|
5
|
+
from .base_partial_3d_array import BasePartial3DArray
|
|
6
|
+
from .util import sanitise_slice, to_slice, unpack_slice, sanitise_unbounded_slice
|
|
7
|
+
from .data_types import DtypeType, Integer, IntegerType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class UnboundedPartial3DArray(BasePartial3DArray):
|
|
11
|
+
"""
|
|
12
|
+
This is designed to work similarly to a numpy.ndarray but stores the data in a very different way.
|
|
13
|
+
A numpy.ndarray stores a fixed size continuous array which for large arrays can become unmanageable.
|
|
14
|
+
Sparse arrays allow individual values to exist which can be great where a small set of values are
|
|
15
|
+
defined in a large area but get less efficient the denser the defined values are.
|
|
16
|
+
|
|
17
|
+
This class was born out of the need for an array that has a fixed size in the horizontal directions but an
|
|
18
|
+
unlimited height in the vertical distance (both above and below the origin)
|
|
19
|
+
This is achieved by splitting the array into sections of fixed height and storing them sparsely so that only
|
|
20
|
+
the defined sections need to be held in memory.
|
|
21
|
+
|
|
22
|
+
This class implements an API that resembles that of a numpy array but it is not directly compatible with numpy.
|
|
23
|
+
This class also implements methods to access and directly modify the underlying numpy arrays to give finer control.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
dtype: DtypeType,
|
|
29
|
+
default_value: Union[int, bool],
|
|
30
|
+
section_shape: Tuple[int, int, int],
|
|
31
|
+
default_section_counts: Tuple[int, int],
|
|
32
|
+
sections: Optional[Dict[int, numpy.ndarray]] = None,
|
|
33
|
+
):
|
|
34
|
+
"""
|
|
35
|
+
Construct a :class:`UnboundedPartial3DArray`. This should not be used directly. You should use the relevant subclass for your use.
|
|
36
|
+
|
|
37
|
+
:param dtype: The dtype that all arrays will be stored in.
|
|
38
|
+
:param default_value: The default value that all undefined arrays will be populated with if required.
|
|
39
|
+
:param section_shape: The shape of each section array.
|
|
40
|
+
:param default_section_counts: A tuple containing the default number of sections above and below the origin in the y axis. This is used to define the default bounds.
|
|
41
|
+
:param sections: The sections to initialise the array with.
|
|
42
|
+
"""
|
|
43
|
+
super().__init__(
|
|
44
|
+
dtype,
|
|
45
|
+
default_value,
|
|
46
|
+
section_shape,
|
|
47
|
+
(0, None, 0),
|
|
48
|
+
(section_shape[0], None, section_shape[0]),
|
|
49
|
+
(1, 1, 1),
|
|
50
|
+
sections=sections,
|
|
51
|
+
)
|
|
52
|
+
self._default_min_y = -default_section_counts[0] * self.section_shape[1]
|
|
53
|
+
self._default_max_y = default_section_counts[1] * self.section_shape[1]
|
|
54
|
+
|
|
55
|
+
def __repr__(self):
|
|
56
|
+
return f"UnboundedPartial3DArray(dtype={self.dtype}, shape={self.shape})"
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def size_y(self) -> float: # Literal[math.inf]:
|
|
60
|
+
"""The size of the array in the y axis. Is always :attr:`math.inf` for the unbounded variant. Read Only"""
|
|
61
|
+
return math.inf
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def sections(self) -> Iterable[int]:
|
|
65
|
+
"""An iterable of the section defined in the array."""
|
|
66
|
+
return self._sections.keys()
|
|
67
|
+
|
|
68
|
+
def create_section(self, sy: IntegerType):
|
|
69
|
+
"""
|
|
70
|
+
Create a section array at the given location using the default value and dtype.
|
|
71
|
+
|
|
72
|
+
:param sy: The section index to create.
|
|
73
|
+
"""
|
|
74
|
+
self._sections[int(sy)] = numpy.full(
|
|
75
|
+
self.section_shape, self.default_value, dtype=self._dtype
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def has_section(self, sy: int) -> bool:
|
|
79
|
+
"""Check if the array for a given section exists.
|
|
80
|
+
:param cy: The section y index
|
|
81
|
+
:return: True if the array exists, False otherwise
|
|
82
|
+
"""
|
|
83
|
+
return sy in self._sections
|
|
84
|
+
|
|
85
|
+
def add_section(self, sy: IntegerType, section: numpy.ndarray):
|
|
86
|
+
"""
|
|
87
|
+
Add a section array at the given location.
|
|
88
|
+
|
|
89
|
+
:param sy: The section index to assign the array to.
|
|
90
|
+
:param section: The array to assign to the section. The shape must equal :attr:`section_shape`.
|
|
91
|
+
"""
|
|
92
|
+
if section.shape != self._section_shape:
|
|
93
|
+
raise ValueError(
|
|
94
|
+
f"The size of all sections must be equal to the section_shape. Expected shape {self._section_shape}, got {section.shape}"
|
|
95
|
+
)
|
|
96
|
+
if section.dtype != self._dtype:
|
|
97
|
+
section = section.astype(self._dtype)
|
|
98
|
+
self._sections[int(sy)] = section
|
|
99
|
+
|
|
100
|
+
def get_section(self, sy: Union[int, numpy.integer]) -> numpy.ndarray:
|
|
101
|
+
"""
|
|
102
|
+
Get the section array for a given section index.
|
|
103
|
+
|
|
104
|
+
If the section is not defined it will be populated using :meth:`create_section`
|
|
105
|
+
|
|
106
|
+
:param sy: The section index to get.
|
|
107
|
+
:return: Numpy array for this section
|
|
108
|
+
"""
|
|
109
|
+
if sy not in self._sections:
|
|
110
|
+
self.create_section(int(sy))
|
|
111
|
+
return self._sections[sy]
|
|
112
|
+
|
|
113
|
+
def __setitem__(
|
|
114
|
+
self,
|
|
115
|
+
slices: Tuple[
|
|
116
|
+
Union[IntegerType, slice],
|
|
117
|
+
Union[IntegerType, slice],
|
|
118
|
+
Union[IntegerType, slice],
|
|
119
|
+
],
|
|
120
|
+
value: Union[int, numpy.integer, numpy.ndarray],
|
|
121
|
+
):
|
|
122
|
+
"""
|
|
123
|
+
Set a sub-section of the infinite height array.
|
|
124
|
+
|
|
125
|
+
>>> # set the value at a given location
|
|
126
|
+
>>> partial_array[3, 4, 5] = 1
|
|
127
|
+
>>> # set a cuboid volume in the array
|
|
128
|
+
>>> partial_array[2:3, 4:5, 6:7] = 1
|
|
129
|
+
>>> # slice and int can be mixed
|
|
130
|
+
>>> partial_array[2:3, 4, 6:7] = 1
|
|
131
|
+
>>> # if an unbounded slice is given in the y axis it will be capped at the default max and min y values.
|
|
132
|
+
>>> partial_array[2:3, :, 6:7] = 1
|
|
133
|
+
|
|
134
|
+
:param slices: The slices or locations that define the volume to set.
|
|
135
|
+
:param value: The value to set at the given location. Can be an integer or array.
|
|
136
|
+
"""
|
|
137
|
+
if isinstance(value, Integer) and all(isinstance(s, Integer) for s in slices):
|
|
138
|
+
sy, dy = self._section_index(slices[1])
|
|
139
|
+
self.get_section(sy)[(slices[0], dy, slices[2])] = value
|
|
140
|
+
else:
|
|
141
|
+
self[slices][:, :, :] = value
|
|
142
|
+
|
|
143
|
+
@overload
|
|
144
|
+
def __getitem__(self, slices: Tuple[IntegerType, IntegerType, IntegerType]) -> int:
|
|
145
|
+
...
|
|
146
|
+
|
|
147
|
+
@overload
|
|
148
|
+
def __getitem__(
|
|
149
|
+
self,
|
|
150
|
+
slices: Tuple[
|
|
151
|
+
Union[IntegerType, slice],
|
|
152
|
+
Union[IntegerType, slice],
|
|
153
|
+
Union[IntegerType, slice],
|
|
154
|
+
],
|
|
155
|
+
) -> "BoundedPartial3DArray":
|
|
156
|
+
...
|
|
157
|
+
|
|
158
|
+
def __getitem__(self, item):
|
|
159
|
+
"""
|
|
160
|
+
Get a value or sub-section of the unbounded array.
|
|
161
|
+
|
|
162
|
+
>>> # get the value at a given location
|
|
163
|
+
>>> value = partial_array[3, 4, 5] # an integer
|
|
164
|
+
>>> # get a cuboid volume in the array
|
|
165
|
+
>>> value = partial_array[2:3, 4:5, 6:7] # BoundedPartial3DArray
|
|
166
|
+
>>> # slice and int can be mixed
|
|
167
|
+
>>> value = partial_array[2:3, 4, 6:7] # BoundedPartial3DArray
|
|
168
|
+
>>> # if an unbounded slice is given in the y axis it will be capped at the default max and min y values.
|
|
169
|
+
>>> value = partial_array[2:3, :, 6:7] # BoundedPartial3DArray
|
|
170
|
+
|
|
171
|
+
:param item: The slices to extract.
|
|
172
|
+
:return: The value or BoundedPartial3DArray viewing into this array.
|
|
173
|
+
"""
|
|
174
|
+
if isinstance(item, tuple):
|
|
175
|
+
if len(item) != 3:
|
|
176
|
+
raise KeyError(f"Tuple item must be of length 3, got {len(item)}")
|
|
177
|
+
if all(isinstance(i, Integer) for i in item):
|
|
178
|
+
sy = item[1] // self.section_shape[1]
|
|
179
|
+
if sy in self:
|
|
180
|
+
return int(
|
|
181
|
+
self._sections[sy][
|
|
182
|
+
(item[0], item[1] % self.section_shape[1], item[2])
|
|
183
|
+
]
|
|
184
|
+
)
|
|
185
|
+
else:
|
|
186
|
+
return self.default_value
|
|
187
|
+
|
|
188
|
+
elif all(isinstance(i, (int, numpy.integer, slice)) for i in item):
|
|
189
|
+
start_x, stop_x, step_x = sanitise_slice(
|
|
190
|
+
*unpack_slice(to_slice(item[0])), self.size_x
|
|
191
|
+
)
|
|
192
|
+
start_y, stop_y, step_y = sanitise_unbounded_slice(
|
|
193
|
+
*unpack_slice(to_slice(item[1])),
|
|
194
|
+
self._default_min_y,
|
|
195
|
+
self._default_max_y,
|
|
196
|
+
)
|
|
197
|
+
start_z, stop_z, step_z = sanitise_slice(
|
|
198
|
+
*unpack_slice(to_slice(item[2])), self.size_z
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
return BoundedPartial3DArray.from_partial_array(
|
|
202
|
+
self,
|
|
203
|
+
(start_x, start_y, start_z),
|
|
204
|
+
(stop_x, stop_y, stop_z),
|
|
205
|
+
(step_x, step_y, step_z),
|
|
206
|
+
)
|
|
207
|
+
else:
|
|
208
|
+
raise KeyError(f"Unsupported tuple {item} for getitem")
|
|
209
|
+
|
|
210
|
+
else:
|
|
211
|
+
raise KeyError(
|
|
212
|
+
f"{item.__class__.__name__}({item}) is not a supported input for __getitem__"
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
def __array__(self, dtype=None):
|
|
216
|
+
"""
|
|
217
|
+
Get the data contained within as a numpy array.
|
|
218
|
+
|
|
219
|
+
The y axis will be clamped to the default minimum and maximum y values.
|
|
220
|
+
|
|
221
|
+
>>> numpy.array(partial_array)
|
|
222
|
+
|
|
223
|
+
:param dtype: The dtype of the returned numpy array.
|
|
224
|
+
:return: A numpy array of the contained data.
|
|
225
|
+
"""
|
|
226
|
+
return self[:, :, :].__array__(dtype)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
from .bounded_partial_3d_array import BoundedPartial3DArray
|