pylitematic 0.0.1__tar.gz → 0.0.2__tar.gz
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.
- {pylitematic-0.0.1 → pylitematic-0.0.2}/PKG-INFO +1 -1
- {pylitematic-0.0.1 → pylitematic-0.0.2}/pyproject.toml +1 -1
- {pylitematic-0.0.1 → pylitematic-0.0.2}/src/pylitematic/__init__.py +1 -1
- {pylitematic-0.0.1 → pylitematic-0.0.2}/src/pylitematic/geometry.py +34 -10
- {pylitematic-0.0.1 → pylitematic-0.0.2}/src/pylitematic/region.py +141 -27
- {pylitematic-0.0.1 → pylitematic-0.0.2}/src/pylitematic/schematic.py +2 -2
- pylitematic-0.0.2/src/pylitematic/test.py +39 -0
- {pylitematic-0.0.1 → pylitematic-0.0.2}/src/pylitematic.egg-info/PKG-INFO +1 -1
- pylitematic-0.0.1/src/pylitematic/test.py +0 -23
- {pylitematic-0.0.1 → pylitematic-0.0.2}/README.md +0 -0
- {pylitematic-0.0.1 → pylitematic-0.0.2}/setup.cfg +0 -0
- {pylitematic-0.0.1 → pylitematic-0.0.2}/src/pylitematic/block_property.py +0 -0
- {pylitematic-0.0.1 → pylitematic-0.0.2}/src/pylitematic/block_state.py +0 -0
- {pylitematic-0.0.1 → pylitematic-0.0.2}/src/pylitematic/resource_location.py +0 -0
- {pylitematic-0.0.1 → pylitematic-0.0.2}/src/pylitematic.egg-info/SOURCES.txt +0 -0
- {pylitematic-0.0.1 → pylitematic-0.0.2}/src/pylitematic.egg-info/dependency_links.txt +0 -0
- {pylitematic-0.0.1 → pylitematic-0.0.2}/src/pylitematic.egg-info/requires.txt +0 -0
- {pylitematic-0.0.1 → pylitematic-0.0.2}/src/pylitematic.egg-info/top_level.txt +0 -0
- {pylitematic-0.0.1 → pylitematic-0.0.2}/tests/test_block_property.py +0 -0
- {pylitematic-0.0.1 → pylitematic-0.0.2}/tests/test_resource_location.py +0 -0
@@ -19,11 +19,14 @@ class Vec3i:
|
|
19
19
|
def __str__(self) -> str:
|
20
20
|
return str(list(self))
|
21
21
|
|
22
|
+
def __len__(self) -> int:
|
23
|
+
return 3
|
24
|
+
|
22
25
|
def __add__(self, other) -> Vec3i:
|
23
26
|
arr = np.array(self)
|
24
|
-
|
27
|
+
other = self._to_array(other)
|
25
28
|
try:
|
26
|
-
result = arr +
|
29
|
+
result = arr + other
|
27
30
|
except Exception:
|
28
31
|
return NotImplemented
|
29
32
|
return type(self)(*result.astype(int))
|
@@ -33,9 +36,9 @@ class Vec3i:
|
|
33
36
|
|
34
37
|
def __sub__(self, other) -> Vec3i:
|
35
38
|
arr = np.array(self)
|
36
|
-
|
39
|
+
other = self._to_array(other)
|
37
40
|
try:
|
38
|
-
result = arr -
|
41
|
+
result = arr - other
|
39
42
|
except Exception:
|
40
43
|
return NotImplemented
|
41
44
|
return type(self)(*result.astype(int))
|
@@ -48,13 +51,34 @@ class Vec3i:
|
|
48
51
|
return NotImplemented
|
49
52
|
return type(self)(*result.astype(int))
|
50
53
|
|
51
|
-
def __mul__(self,
|
52
|
-
|
53
|
-
|
54
|
+
def __mul__(self, other) -> Vec3i:
|
55
|
+
arr = np.array(self)
|
56
|
+
other = self._to_array(other)
|
57
|
+
try:
|
58
|
+
result = arr * other
|
59
|
+
except Exception:
|
60
|
+
return NotImplemented
|
61
|
+
return type(self)(*result.astype(int))
|
62
|
+
|
63
|
+
def __rmul__(self, other) -> Vec3i:
|
64
|
+
return self.__mul__(other)
|
65
|
+
|
66
|
+
def __floordiv__(self, other) -> Vec3i:
|
67
|
+
arr = np.array(self)
|
68
|
+
other = self._to_array(other)
|
69
|
+
try:
|
70
|
+
result = arr // other
|
71
|
+
except Exception:
|
72
|
+
return NotImplemented
|
73
|
+
return type(self)(*result.astype(int))
|
54
74
|
|
55
|
-
def
|
56
|
-
|
57
|
-
|
75
|
+
def __rfloordiv__(self, other) -> Vec3i:
|
76
|
+
arr = np.array(self)
|
77
|
+
try:
|
78
|
+
result = other // arr
|
79
|
+
except Exception:
|
80
|
+
return NotImplemented
|
81
|
+
return type(self)(*result.astype(int))
|
58
82
|
|
59
83
|
def __neg__(self) -> Vec3i:
|
60
84
|
return type(self)(-self._a, -self._b, -self._c)
|
@@ -1,12 +1,15 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
from bitpacking import bitpack, bitunpack
|
4
|
+
from functools import cached_property
|
4
5
|
import nbtlib
|
5
6
|
import numpy as np
|
6
7
|
import twos
|
8
|
+
from typing import Iterator
|
7
9
|
|
8
10
|
from .block_state import BlockState
|
9
11
|
from .geometry import BlockPosition, Size3D
|
12
|
+
from .resource_location import ResourceLocation
|
10
13
|
|
11
14
|
|
12
15
|
AIR = BlockState("air")
|
@@ -22,27 +25,77 @@ class Region:
|
|
22
25
|
self._size: Size3D = Size3D(*size)
|
23
26
|
|
24
27
|
self._palette: list[BlockState] = [AIR]
|
25
|
-
self._palette_map: dict[BlockState, int] = {AIR: 0}
|
26
|
-
self._blocks = np.zeros(abs(self._size))
|
28
|
+
self._palette_map: dict[BlockState, int] = {AIR: 0}
|
29
|
+
self._blocks = np.zeros(abs(self._size), dtype=int)
|
30
|
+
|
27
31
|
self._entities: list[nbtlib.Compound] = []
|
28
32
|
self._tile_entities: list[nbtlib.Compound] = []
|
29
33
|
self._block_ticks: list[nbtlib.Compound] = []
|
30
34
|
self._fluid_ticks: list[nbtlib.Compound] = []
|
31
35
|
|
32
|
-
def
|
36
|
+
def __contains__(self, item) -> bool:
|
37
|
+
if isinstance(item, BlockPosition):
|
38
|
+
return all(self.lower <= item) and all(item <= self.upper)
|
39
|
+
elif isinstance(item, BlockState):
|
40
|
+
index = self._palette_map.get(item)
|
41
|
+
if index is None:
|
42
|
+
return False
|
43
|
+
return np.any(self._blocks == index)
|
44
|
+
elif isinstance(item, ResourceLocation):
|
45
|
+
return any(
|
46
|
+
(bs.id == item and np.any(self._blocks == idx))
|
47
|
+
for bs, idx in self._palette_map.items())
|
48
|
+
else:
|
49
|
+
return False
|
50
|
+
|
51
|
+
def _expand_index(self, index):
|
52
|
+
if not isinstance(index, tuple):
|
53
|
+
index = (index,)
|
54
|
+
ndim = self._blocks.ndim
|
55
|
+
result = []
|
56
|
+
for item in index:
|
57
|
+
if item is Ellipsis:
|
58
|
+
result.extend([slice(None)] * (ndim - len(index) + 1))
|
59
|
+
else:
|
60
|
+
result.append(item)
|
61
|
+
while len(result) < ndim:
|
62
|
+
result.append(slice(None))
|
63
|
+
return tuple(result)
|
64
|
+
|
65
|
+
def _to_internal(self, pos):
|
66
|
+
index = []
|
67
|
+
for i, item in enumerate(pos):
|
68
|
+
offset = self.lower[i]
|
69
|
+
if isinstance(item, int):
|
70
|
+
index.append(item - offset)
|
71
|
+
elif isinstance(item, slice):
|
72
|
+
start = item.start - offset if item.start is not None else None
|
73
|
+
stop = item.stop - offset if item.stop is not None else None
|
74
|
+
index.append(slice(start, stop, item.step))
|
75
|
+
else:
|
76
|
+
index.append(item)
|
77
|
+
return tuple(index)
|
78
|
+
|
79
|
+
def _from_internal(self, index: tuple[int, int, int]) -> BlockPosition:
|
80
|
+
return self.lower + index
|
81
|
+
|
82
|
+
def _key_to_index(self, key):
|
33
83
|
if isinstance(key, BlockPosition):
|
34
|
-
index =
|
35
|
-
|
84
|
+
index = tuple(key)
|
85
|
+
else:
|
86
|
+
index = self._expand_index(key)
|
87
|
+
return self._to_internal(index)
|
36
88
|
|
37
|
-
|
89
|
+
def __getitem__(self, key):
|
90
|
+
index = self._key_to_index(key)
|
91
|
+
indices = self._blocks[index]
|
38
92
|
if np.isscalar(indices):
|
39
93
|
return self._palette[indices]
|
40
94
|
|
41
95
|
return np.array(self._palette, dtype=object)[indices]
|
42
96
|
|
43
97
|
def __setitem__(self, key, value) -> None:
|
44
|
-
|
45
|
-
key = key.to_tuple()
|
98
|
+
index = self._key_to_index(key)
|
46
99
|
|
47
100
|
if isinstance(value, list):
|
48
101
|
value = np.array(value, dtype=object)
|
@@ -52,11 +105,10 @@ class Region:
|
|
52
105
|
if value not in self._palette_map:
|
53
106
|
self._palette_map[value] = len(self._palette)
|
54
107
|
self._palette.append(value)
|
55
|
-
index = self._palette_map[value]
|
56
|
-
self._blocks[key] = index
|
108
|
+
self._blocks[index] = self._palette_map[value]
|
57
109
|
|
58
110
|
elif isinstance(value, np.ndarray):
|
59
|
-
if value.shape != self._blocks[
|
111
|
+
if value.shape != self._blocks[index].shape:
|
60
112
|
raise ValueError(
|
61
113
|
"Shape mismatch between assigned array and target slice")
|
62
114
|
|
@@ -69,7 +121,7 @@ class Region:
|
|
69
121
|
self._palette.append(state)
|
70
122
|
idx.append(self._palette_map[state])
|
71
123
|
index_array = np.array(idx, dtype=int)[xdi].reshape(value.shape)
|
72
|
-
self._blocks[
|
124
|
+
self._blocks[index] = index_array
|
73
125
|
else:
|
74
126
|
raise TypeError(
|
75
127
|
"Value must be a BlockState or a list of BlockStates")
|
@@ -119,18 +171,37 @@ class Region:
|
|
119
171
|
)
|
120
172
|
return nbtlib.LongArray([twos.to_signed(x, 64) for x in chunks])
|
121
173
|
|
174
|
+
@staticmethod
|
175
|
+
def inclusive_end(
|
176
|
+
size: Size3D,
|
177
|
+
pos: BlockPosition = BlockPosition(0, 0, 0),
|
178
|
+
) -> BlockPosition:
|
179
|
+
return pos + (size - np.sign(size))
|
180
|
+
# return pos + np.where(size > 0, size - 1, size + 1)
|
181
|
+
|
122
182
|
@property
|
123
183
|
def size(self) -> Size3D:
|
124
184
|
return self._size
|
125
185
|
|
186
|
+
@cached_property
|
187
|
+
def sign(self) -> Size3D:
|
188
|
+
return Size3D(*np.sign(self._size))
|
189
|
+
|
126
190
|
@property
|
127
191
|
def origin(self) -> BlockPosition:
|
128
192
|
return self._origin
|
129
193
|
|
130
|
-
@
|
194
|
+
@cached_property
|
195
|
+
def limit(self) -> BlockPosition:
|
196
|
+
return self.inclusive_end(pos=self._origin, size=self._size)
|
197
|
+
|
198
|
+
@cached_property
|
199
|
+
def start(self) -> BlockPosition:
|
200
|
+
return BlockPosition(0, 0, 0)
|
201
|
+
|
202
|
+
@cached_property
|
131
203
|
def end(self) -> BlockPosition:
|
132
|
-
return self.
|
133
|
-
self._size > 0, self._size - 1, self._size + 1)
|
204
|
+
return self.inclusive_end(pos=self.start, size=self._size)
|
134
205
|
|
135
206
|
@property
|
136
207
|
def width(self) -> int:
|
@@ -149,33 +220,76 @@ class Region:
|
|
149
220
|
return np.prod(self.shape).item()
|
150
221
|
|
151
222
|
@property
|
152
|
-
def
|
223
|
+
def block_count(self) -> int:
|
224
|
+
# TODO: Add filter BlockState and rename to count()
|
153
225
|
return np.count_nonzero(self._blocks)
|
154
226
|
|
155
227
|
@property
|
156
228
|
def shape(self) -> tuple[int, int, int]:
|
157
229
|
return self._blocks.shape
|
158
230
|
|
159
|
-
@
|
231
|
+
@cached_property
|
160
232
|
def lower(self) -> BlockPosition:
|
161
|
-
return BlockPosition(*np.min((self.
|
233
|
+
return BlockPosition(*np.min((self.start, self.end), axis=0))
|
162
234
|
|
163
|
-
@
|
235
|
+
@cached_property
|
164
236
|
def upper(self) -> BlockPosition:
|
165
|
-
return BlockPosition(*np.max((self.
|
237
|
+
return BlockPosition(*np.max((self.start, self.end), axis=0))
|
166
238
|
|
167
|
-
@
|
239
|
+
@cached_property
|
168
240
|
def bounds(self) -> tuple[BlockPosition, BlockPosition]:
|
169
241
|
return self.lower, self.upper
|
170
242
|
|
171
|
-
|
172
|
-
|
243
|
+
@cached_property
|
244
|
+
def global_lower(self) -> BlockPosition:
|
245
|
+
return BlockPosition(*np.min((self.origin, self.limit), axis=0))
|
173
246
|
|
174
|
-
|
175
|
-
|
247
|
+
@cached_property
|
248
|
+
def global_upper(self) -> BlockPosition:
|
249
|
+
return BlockPosition(*np.max((self.origin, self.limit), axis=0))
|
176
250
|
|
177
|
-
|
178
|
-
|
251
|
+
@cached_property
|
252
|
+
def global_bounds(self) -> tuple[BlockPosition, BlockPosition]:
|
253
|
+
return self.global_lower, self.global_upper
|
254
|
+
|
255
|
+
def blocks(
|
256
|
+
self,
|
257
|
+
include: BlockState | list[BlockState] | None = None,
|
258
|
+
exclude: BlockState | list[BlockState] | None = None,
|
259
|
+
ignore_props: bool = False,
|
260
|
+
) -> Iterator[tuple[BlockPosition, BlockState]]:
|
261
|
+
if isinstance(include, BlockState):
|
262
|
+
include = [include]
|
263
|
+
if isinstance(exclude, BlockState):
|
264
|
+
exclude = [exclude]
|
265
|
+
|
266
|
+
for z, y, x in np.ndindex(self.shape[::-1]):
|
267
|
+
pos = BlockPosition(x, y, z) * self.sign
|
268
|
+
state = self[pos]
|
269
|
+
|
270
|
+
if exclude:
|
271
|
+
if not ignore_props:
|
272
|
+
if state in exclude:
|
273
|
+
continue
|
274
|
+
else:
|
275
|
+
if any(state.id == ex.id for ex in exclude):
|
276
|
+
continue
|
277
|
+
|
278
|
+
if include:
|
279
|
+
if not ignore_props:
|
280
|
+
if state not in include:
|
281
|
+
continue
|
282
|
+
else:
|
283
|
+
if not any(state.id == s.id for s in include):
|
284
|
+
continue
|
285
|
+
|
286
|
+
yield pos, state
|
287
|
+
|
288
|
+
def to_global(self, local_pos: BlockPosition) -> BlockPosition:
|
289
|
+
return self._origin + local_pos
|
290
|
+
|
291
|
+
def to_local(self, global_pos: BlockPosition) -> BlockPosition:
|
292
|
+
return global_pos - self._origin
|
179
293
|
|
180
294
|
def to_nbt(self) -> nbtlib.Compound:
|
181
295
|
nbt = nbtlib.Compound()
|
@@ -107,7 +107,7 @@ class Schematic:
|
|
107
107
|
lowers = []
|
108
108
|
uppers = []
|
109
109
|
for reg in self._regions.values():
|
110
|
-
lower, upper = reg.
|
110
|
+
lower, upper = reg.global_bounds
|
111
111
|
lowers.append(lower)
|
112
112
|
uppers.append(upper)
|
113
113
|
return (
|
@@ -121,7 +121,7 @@ class Schematic:
|
|
121
121
|
|
122
122
|
@property
|
123
123
|
def blocks(self) -> int:
|
124
|
-
return sum(reg.
|
124
|
+
return sum(reg.block_count for reg in self._regions.values())
|
125
125
|
|
126
126
|
@property
|
127
127
|
def region_count(self) -> int:
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import pathlib
|
3
|
+
from pylitematic import BlockPosition, BlockState, ResourceLocation, Schematic
|
4
|
+
|
5
|
+
path = pathlib.Path("/mnt/d/minecraft/schematics/Litematica/test/subs.litematic")
|
6
|
+
path = pathlib.Path("/mnt/d/minecraft/schematics/Litematica/test/creeper_test.litematic")
|
7
|
+
path = pathlib.Path("/mnt/d/minecraft/schematics/Litematica/test/regions.litematic")
|
8
|
+
stone = BlockState.from_string("minecraft:stone")
|
9
|
+
dirt = BlockState.from_string("minecraft:dirt")
|
10
|
+
s = Schematic.load(path)
|
11
|
+
print(f"{s.volume=} {s.size=} {s.bounds=}")
|
12
|
+
for name, reg in s.regions():
|
13
|
+
print(name)
|
14
|
+
print(f"\t{reg.shape=} {reg.volume=} {reg.block_count=}")
|
15
|
+
print(f"\t{reg.origin=!s} {reg.limit=!s}")
|
16
|
+
print(f"\t{reg.start=!s} {reg.end=!s}")
|
17
|
+
print(f"\t{reg.lower=!s} {reg.upper=!s} {reg.size=}")
|
18
|
+
# print(f"\t{reg[..., 1, 0]}")
|
19
|
+
# print(f"\t{reg[:][1][0]}")
|
20
|
+
# print(f"\t{reg[BlockPosition(0, 1, 0)]}")
|
21
|
+
# reg[1,1,1] = BlockState.from_string("minecraft:stone")
|
22
|
+
# print("lol: ", reg[reg.end])
|
23
|
+
reg[0,:,0] = BlockState("minecraft:obsidian")
|
24
|
+
reg[0,:,0] = [dirt, stone, dirt]
|
25
|
+
# print(reg[...,0])
|
26
|
+
# print(reg[np.array([BlockPosition(0, 0, 0), BlockPosition(1, 1, 1)])])
|
27
|
+
# print(f"\t{reg[:]}")
|
28
|
+
# for pos, state in reg.blocks(exclude_air=True):
|
29
|
+
# print(pos, state)
|
30
|
+
# for pos, state in reg.blocks((BlockState("oak_log", axis="x"), BlockState("spruce_log", axis="z")), ignore_props=True):
|
31
|
+
for pos, state in reg.blocks(exclude=BlockState("air")):
|
32
|
+
print(pos, reg._to_internal(pos), state)
|
33
|
+
for pos, state in reg.blocks(include=BlockState("air")):
|
34
|
+
reg[pos] = BlockState("minecraft:netherrack")
|
35
|
+
print(BlockState("oak_log", axis="x") in reg)
|
36
|
+
print(BlockPosition(1, 1, 0) in reg)
|
37
|
+
print(ResourceLocation("birch_log") in reg)
|
38
|
+
# print(reg[0,:,2])
|
39
|
+
s.save("/mnt/d/minecraft/schematics/Litematica/test/aaa.litematic")
|
@@ -1,23 +0,0 @@
|
|
1
|
-
import pathlib
|
2
|
-
from pylitematic import BlockPosition, BlockState, Schematic
|
3
|
-
|
4
|
-
|
5
|
-
path = pathlib.Path("/mnt/d/minecraft/schematics/Litematica/test/subs.litematic")
|
6
|
-
path = pathlib.Path("/mnt/d/minecraft/schematics/Litematica/test/creeper_test.litematic")
|
7
|
-
path = pathlib.Path("/mnt/d/minecraft/schematics/Litematica/test/regions.litematic")
|
8
|
-
stone = BlockState.from_string("minecraft:stone")
|
9
|
-
dirt = BlockState.from_string("minecraft:dirt")
|
10
|
-
s = Schematic.load(path)
|
11
|
-
print(f"{s.volume=} {s.size=} {s.bounds=}")
|
12
|
-
for name, reg in s.regions():
|
13
|
-
print(name)
|
14
|
-
print(f"\t{reg.shape=} {reg.volume=} {reg.blocks=}")
|
15
|
-
print(f"\t{reg.origin=!s} {reg.end=!s}")
|
16
|
-
print(f"\t{reg.lower=} {reg.upper=!s} {reg.size=}")
|
17
|
-
# print(f"\t{reg[..., 1, 0]}")
|
18
|
-
# print(f"\t{reg[:][1][0]}")
|
19
|
-
# print(f"\t{reg[BlockPosition(0, 1, 0)]}")
|
20
|
-
reg[1,1,1] = BlockState.from_string("minecraft:stone")
|
21
|
-
reg[0,:,0] = [dirt, stone, dirt]
|
22
|
-
print(f"\t{reg[:]}")
|
23
|
-
s.save("/mnt/d/minecraft/schematics/Litematica/test/pylitematic.litematic")
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|