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.

Files changed (198) hide show
  1. amulet/__init__.py +27 -27
  2. amulet/__pyinstaller/__init__.py +2 -2
  3. amulet/__pyinstaller/hook-amulet.py +4 -4
  4. amulet/_version.py +21 -21
  5. amulet/api/__init__.py +2 -2
  6. amulet/api/abstract_base_entity.py +128 -128
  7. amulet/api/block.py +630 -630
  8. amulet/api/block_entity.py +71 -71
  9. amulet/api/cache.py +107 -107
  10. amulet/api/chunk/__init__.py +6 -6
  11. amulet/api/chunk/biomes.py +207 -207
  12. amulet/api/chunk/block_entity_dict.py +175 -175
  13. amulet/api/chunk/blocks.py +46 -46
  14. amulet/api/chunk/chunk.py +389 -389
  15. amulet/api/chunk/entity_list.py +75 -75
  16. amulet/api/chunk/status.py +167 -167
  17. amulet/api/data_types/__init__.py +4 -4
  18. amulet/api/data_types/generic_types.py +4 -4
  19. amulet/api/data_types/operation_types.py +16 -16
  20. amulet/api/data_types/world_types.py +49 -49
  21. amulet/api/data_types/wrapper_types.py +71 -71
  22. amulet/api/entity.py +74 -74
  23. amulet/api/errors.py +119 -119
  24. amulet/api/history/__init__.py +36 -36
  25. amulet/api/history/base/__init__.py +3 -3
  26. amulet/api/history/base/base_history.py +26 -26
  27. amulet/api/history/base/history_manager.py +63 -63
  28. amulet/api/history/base/revision_manager.py +73 -73
  29. amulet/api/history/changeable.py +15 -15
  30. amulet/api/history/data_types.py +7 -7
  31. amulet/api/history/history_manager/__init__.py +3 -3
  32. amulet/api/history/history_manager/container.py +102 -102
  33. amulet/api/history/history_manager/database.py +279 -279
  34. amulet/api/history/history_manager/meta.py +93 -93
  35. amulet/api/history/history_manager/object.py +116 -116
  36. amulet/api/history/revision_manager/__init__.py +2 -2
  37. amulet/api/history/revision_manager/disk.py +33 -33
  38. amulet/api/history/revision_manager/ram.py +12 -12
  39. amulet/api/item.py +75 -75
  40. amulet/api/level/__init__.py +4 -4
  41. amulet/api/level/base_level/__init__.py +1 -1
  42. amulet/api/level/base_level/base_level.py +1035 -1026
  43. amulet/api/level/base_level/chunk_manager.py +227 -227
  44. amulet/api/level/base_level/clone.py +389 -389
  45. amulet/api/level/base_level/player_manager.py +101 -101
  46. amulet/api/level/immutable_structure/__init__.py +1 -1
  47. amulet/api/level/immutable_structure/immutable_structure.py +94 -94
  48. amulet/api/level/immutable_structure/void_format_wrapper.py +117 -117
  49. amulet/api/level/structure.py +22 -22
  50. amulet/api/level/world.py +19 -19
  51. amulet/api/partial_3d_array/__init__.py +2 -2
  52. amulet/api/partial_3d_array/base_partial_3d_array.py +263 -263
  53. amulet/api/partial_3d_array/bounded_partial_3d_array.py +528 -528
  54. amulet/api/partial_3d_array/data_types.py +15 -15
  55. amulet/api/partial_3d_array/unbounded_partial_3d_array.py +229 -229
  56. amulet/api/partial_3d_array/util.py +152 -152
  57. amulet/api/player.py +65 -65
  58. amulet/api/registry/__init__.py +2 -2
  59. amulet/api/registry/base_registry.py +34 -34
  60. amulet/api/registry/biome_manager.py +153 -153
  61. amulet/api/registry/block_manager.py +156 -156
  62. amulet/api/selection/__init__.py +2 -2
  63. amulet/api/selection/abstract_selection.py +315 -315
  64. amulet/api/selection/box.py +805 -805
  65. amulet/api/selection/group.py +488 -488
  66. amulet/api/structure.py +37 -37
  67. amulet/api/wrapper/__init__.py +8 -8
  68. amulet/api/wrapper/chunk/interface.py +441 -441
  69. amulet/api/wrapper/chunk/translator.py +567 -567
  70. amulet/api/wrapper/format_wrapper.py +772 -772
  71. amulet/api/wrapper/structure_format_wrapper.py +116 -116
  72. amulet/api/wrapper/world_format_wrapper.py +63 -63
  73. amulet/level/__init__.py +1 -1
  74. amulet/level/formats/anvil_forge_world.py +40 -40
  75. amulet/level/formats/anvil_world/__init__.py +3 -3
  76. amulet/level/formats/anvil_world/_sector_manager.py +291 -384
  77. amulet/level/formats/anvil_world/data_pack/__init__.py +2 -2
  78. amulet/level/formats/anvil_world/data_pack/data_pack.py +224 -224
  79. amulet/level/formats/anvil_world/data_pack/data_pack_manager.py +77 -77
  80. amulet/level/formats/anvil_world/dimension.py +177 -177
  81. amulet/level/formats/anvil_world/format.py +769 -769
  82. amulet/level/formats/anvil_world/region.py +384 -384
  83. amulet/level/formats/construction/__init__.py +3 -3
  84. amulet/level/formats/construction/format_wrapper.py +515 -515
  85. amulet/level/formats/construction/interface.py +134 -134
  86. amulet/level/formats/construction/section.py +60 -60
  87. amulet/level/formats/construction/util.py +165 -165
  88. amulet/level/formats/leveldb_world/__init__.py +3 -3
  89. amulet/level/formats/leveldb_world/chunk.py +33 -33
  90. amulet/level/formats/leveldb_world/dimension.py +385 -419
  91. amulet/level/formats/leveldb_world/format.py +659 -641
  92. amulet/level/formats/leveldb_world/interface/chunk/__init__.py +36 -36
  93. amulet/level/formats/leveldb_world/interface/chunk/base_leveldb_interface.py +836 -836
  94. amulet/level/formats/leveldb_world/interface/chunk/generate_interface.py +31 -31
  95. amulet/level/formats/leveldb_world/interface/chunk/leveldb_0.py +30 -30
  96. amulet/level/formats/leveldb_world/interface/chunk/leveldb_1.py +12 -12
  97. amulet/level/formats/leveldb_world/interface/chunk/leveldb_10.py +12 -12
  98. amulet/level/formats/leveldb_world/interface/chunk/leveldb_11.py +12 -12
  99. amulet/level/formats/leveldb_world/interface/chunk/leveldb_12.py +12 -12
  100. amulet/level/formats/leveldb_world/interface/chunk/leveldb_13.py +12 -12
  101. amulet/level/formats/leveldb_world/interface/chunk/leveldb_14.py +12 -12
  102. amulet/level/formats/leveldb_world/interface/chunk/leveldb_15.py +12 -12
  103. amulet/level/formats/leveldb_world/interface/chunk/leveldb_16.py +12 -12
  104. amulet/level/formats/leveldb_world/interface/chunk/leveldb_17.py +12 -12
  105. amulet/level/formats/leveldb_world/interface/chunk/leveldb_18.py +12 -12
  106. amulet/level/formats/leveldb_world/interface/chunk/leveldb_19.py +12 -12
  107. amulet/level/formats/leveldb_world/interface/chunk/leveldb_2.py +12 -12
  108. amulet/level/formats/leveldb_world/interface/chunk/leveldb_20.py +12 -12
  109. amulet/level/formats/leveldb_world/interface/chunk/leveldb_21.py +12 -12
  110. amulet/level/formats/leveldb_world/interface/chunk/leveldb_22.py +12 -12
  111. amulet/level/formats/leveldb_world/interface/chunk/leveldb_23.py +10 -10
  112. amulet/level/formats/leveldb_world/interface/chunk/leveldb_24.py +10 -10
  113. amulet/level/formats/leveldb_world/interface/chunk/leveldb_25.py +24 -24
  114. amulet/level/formats/leveldb_world/interface/chunk/leveldb_26.py +10 -10
  115. amulet/level/formats/leveldb_world/interface/chunk/leveldb_27.py +10 -10
  116. amulet/level/formats/leveldb_world/interface/chunk/leveldb_28.py +10 -10
  117. amulet/level/formats/leveldb_world/interface/chunk/leveldb_29.py +33 -33
  118. amulet/level/formats/leveldb_world/interface/chunk/leveldb_3.py +57 -57
  119. amulet/level/formats/leveldb_world/interface/chunk/leveldb_30.py +10 -10
  120. amulet/level/formats/leveldb_world/interface/chunk/leveldb_31.py +10 -10
  121. amulet/level/formats/leveldb_world/interface/chunk/leveldb_32.py +10 -10
  122. amulet/level/formats/leveldb_world/interface/chunk/leveldb_33.py +10 -10
  123. amulet/level/formats/leveldb_world/interface/chunk/leveldb_34.py +10 -10
  124. amulet/level/formats/leveldb_world/interface/chunk/leveldb_35.py +10 -10
  125. amulet/level/formats/leveldb_world/interface/chunk/leveldb_36.py +10 -10
  126. amulet/level/formats/leveldb_world/interface/chunk/leveldb_37.py +10 -10
  127. amulet/level/formats/leveldb_world/interface/chunk/leveldb_38.py +10 -10
  128. amulet/level/formats/leveldb_world/interface/chunk/leveldb_39.py +12 -12
  129. amulet/level/formats/leveldb_world/interface/chunk/leveldb_4.py +12 -12
  130. amulet/level/formats/leveldb_world/interface/chunk/leveldb_40.py +16 -16
  131. amulet/level/formats/leveldb_world/interface/chunk/leveldb_5.py +12 -12
  132. amulet/level/formats/leveldb_world/interface/chunk/leveldb_6.py +12 -12
  133. amulet/level/formats/leveldb_world/interface/chunk/leveldb_7.py +12 -12
  134. amulet/level/formats/leveldb_world/interface/chunk/leveldb_8.py +180 -180
  135. amulet/level/formats/leveldb_world/interface/chunk/leveldb_9.py +18 -18
  136. amulet/level/formats/leveldb_world/interface/chunk/leveldb_chunk_versions.py +79 -79
  137. amulet/level/formats/mcstructure/__init__.py +3 -3
  138. amulet/level/formats/mcstructure/chunk.py +50 -50
  139. amulet/level/formats/mcstructure/format_wrapper.py +408 -408
  140. amulet/level/formats/mcstructure/interface.py +175 -175
  141. amulet/level/formats/schematic/__init__.py +3 -3
  142. amulet/level/formats/schematic/chunk.py +55 -55
  143. amulet/level/formats/schematic/data_types.py +4 -4
  144. amulet/level/formats/schematic/format_wrapper.py +373 -373
  145. amulet/level/formats/schematic/interface.py +142 -142
  146. amulet/level/formats/sponge_schem/__init__.py +4 -4
  147. amulet/level/formats/sponge_schem/chunk.py +62 -62
  148. amulet/level/formats/sponge_schem/format_wrapper.py +463 -463
  149. amulet/level/formats/sponge_schem/interface.py +118 -118
  150. amulet/level/formats/sponge_schem/varint/__init__.py +1 -1
  151. amulet/level/formats/sponge_schem/varint/varint.py +87 -87
  152. amulet/level/interfaces/chunk/anvil/anvil_0.py +72 -72
  153. amulet/level/interfaces/chunk/anvil/anvil_1444.py +336 -336
  154. amulet/level/interfaces/chunk/anvil/anvil_1466.py +94 -94
  155. amulet/level/interfaces/chunk/anvil/anvil_1467.py +37 -37
  156. amulet/level/interfaces/chunk/anvil/anvil_1484.py +20 -20
  157. amulet/level/interfaces/chunk/anvil/anvil_1503.py +20 -20
  158. amulet/level/interfaces/chunk/anvil/anvil_1519.py +34 -34
  159. amulet/level/interfaces/chunk/anvil/anvil_1901.py +20 -20
  160. amulet/level/interfaces/chunk/anvil/anvil_1908.py +20 -20
  161. amulet/level/interfaces/chunk/anvil/anvil_1912.py +21 -21
  162. amulet/level/interfaces/chunk/anvil/anvil_1934.py +20 -20
  163. amulet/level/interfaces/chunk/anvil/anvil_2203.py +69 -69
  164. amulet/level/interfaces/chunk/anvil/anvil_2529.py +19 -19
  165. amulet/level/interfaces/chunk/anvil/anvil_2681.py +76 -76
  166. amulet/level/interfaces/chunk/anvil/anvil_2709.py +19 -19
  167. amulet/level/interfaces/chunk/anvil/anvil_2844.py +267 -267
  168. amulet/level/interfaces/chunk/anvil/anvil_3463.py +19 -19
  169. amulet/level/interfaces/chunk/anvil/anvil_na.py +607 -607
  170. amulet/level/interfaces/chunk/anvil/base_anvil_interface.py +326 -326
  171. amulet/level/load.py +59 -59
  172. amulet/level/loader.py +95 -95
  173. amulet/level/translators/chunk/bedrock/__init__.py +267 -267
  174. amulet/level/translators/chunk/bedrock/bedrock_nbt_blockstate_translator.py +46 -46
  175. amulet/level/translators/chunk/bedrock/bedrock_numerical_translator.py +39 -39
  176. amulet/level/translators/chunk/bedrock/bedrock_psudo_numerical_translator.py +37 -37
  177. amulet/level/translators/chunk/java/java_1_18_translator.py +40 -40
  178. amulet/level/translators/chunk/java/java_blockstate_translator.py +94 -94
  179. amulet/level/translators/chunk/java/java_numerical_translator.py +62 -62
  180. amulet/libs/leveldb/__init__.py +7 -7
  181. amulet/operations/__init__.py +5 -5
  182. amulet/operations/clone.py +18 -18
  183. amulet/operations/delete_chunk.py +32 -32
  184. amulet/operations/fill.py +30 -30
  185. amulet/operations/paste.py +65 -65
  186. amulet/operations/replace.py +58 -58
  187. amulet/utils/__init__.py +14 -14
  188. amulet/utils/format_utils.py +41 -41
  189. amulet/utils/generator.py +15 -15
  190. amulet/utils/matrix.py +243 -243
  191. amulet/utils/numpy_helpers.py +46 -46
  192. amulet/utils/world_utils.py +349 -349
  193. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/METADATA +97 -97
  194. amulet_core-1.9.20.dist-info/RECORD +208 -0
  195. amulet_core-1.9.19.dist-info/RECORD +0 -208
  196. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/WHEEL +0 -0
  197. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/entry_points.txt +0 -0
  198. {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