pylitematic 0.0.2__tar.gz → 0.0.4__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.2 → pylitematic-0.0.4}/PKG-INFO +7 -1
- {pylitematic-0.0.2 → pylitematic-0.0.4}/pyproject.toml +11 -2
- pylitematic-0.0.4/src/pylitematic/__init__.py +7 -0
- {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic/block_property.py +13 -79
- {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic/block_state.py +20 -13
- pylitematic-0.0.4/src/pylitematic/geometry.py +171 -0
- pylitematic-0.0.4/src/pylitematic/region.py +485 -0
- {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic/resource_location.py +10 -6
- {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic/schematic.py +18 -13
- pylitematic-0.0.4/src/pylitematic/test.py +72 -0
- {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic.egg-info/PKG-INFO +7 -1
- {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic.egg-info/SOURCES.txt +2 -1
- {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic.egg-info/requires.txt +4 -0
- {pylitematic-0.0.2 → pylitematic-0.0.4}/tests/test_block_property.py +0 -42
- pylitematic-0.0.4/tests/test_schematic.py +40 -0
- pylitematic-0.0.2/src/pylitematic/__init__.py +0 -7
- pylitematic-0.0.2/src/pylitematic/geometry.py +0 -193
- pylitematic-0.0.2/src/pylitematic/region.py +0 -328
- pylitematic-0.0.2/src/pylitematic/test.py +0 -39
- {pylitematic-0.0.2 → pylitematic-0.0.4}/README.md +0 -0
- {pylitematic-0.0.2 → pylitematic-0.0.4}/setup.cfg +0 -0
- {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic.egg-info/dependency_links.txt +0 -0
- {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic.egg-info/top_level.txt +0 -0
- {pylitematic-0.0.2 → pylitematic-0.0.4}/tests/test_resource_location.py +0 -0
@@ -1,18 +1,24 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pylitematic
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.4
|
4
4
|
Summary: Load, modify, and save Litematica schematics
|
5
5
|
Author-email: Boscawinks <bosca.winks@gmx.de>
|
6
6
|
License: GPL-3.0-only
|
7
7
|
Project-URL: Homepage, https://github.com/boscawinks/pylitematic
|
8
8
|
Project-URL: Repository, https://github.com/boscawinks/pylitematic
|
9
9
|
Project-URL: Issues, https://github.com/boscawinks/pylitematic/issues
|
10
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
10
11
|
Classifier: Programming Language :: Python :: 3
|
12
|
+
Classifier: Operating System :: OS Independent
|
13
|
+
Classifier: Topic :: Utilities
|
11
14
|
Requires-Python: >=3.10.12
|
12
15
|
Description-Content-Type: text/markdown
|
13
16
|
Requires-Dist: bitpacking>=0.1.0
|
14
17
|
Requires-Dist: nbtlib>=2.0.4
|
15
18
|
Requires-Dist: numpy>=2.2.6
|
19
|
+
Requires-Dist: twos>=0.0.1
|
20
|
+
Provides-Extra: dev
|
21
|
+
Requires-Dist: pytest; extra == "dev"
|
16
22
|
|
17
23
|
# pylitematic
|
18
24
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "pylitematic"
|
7
|
-
version = "0.0.
|
7
|
+
version = "0.0.4"
|
8
8
|
description = "Load, modify, and save Litematica schematics"
|
9
9
|
authors = [
|
10
10
|
{ name="Boscawinks", email="bosca.winks@gmx.de" }
|
@@ -12,13 +12,22 @@ authors = [
|
|
12
12
|
dependencies = [
|
13
13
|
"bitpacking >=0.1.0",
|
14
14
|
"nbtlib >=2.0.4",
|
15
|
-
"numpy >=2.2.6"
|
15
|
+
"numpy >=2.2.6",
|
16
|
+
"twos >= 0.0.1"
|
16
17
|
]
|
17
18
|
readme = "README.md"
|
18
19
|
requires-python = ">=3.10.12"
|
19
20
|
license = { text = "GPL-3.0-only" }
|
20
21
|
classifiers = [
|
22
|
+
"Development Status :: 2 - Pre-Alpha",
|
21
23
|
"Programming Language :: Python :: 3",
|
24
|
+
"Operating System :: OS Independent",
|
25
|
+
"Topic :: Utilities"
|
26
|
+
]
|
27
|
+
|
28
|
+
[project.optional-dependencies]
|
29
|
+
dev = [
|
30
|
+
"pytest"
|
22
31
|
]
|
23
32
|
|
24
33
|
[project.urls]
|
@@ -14,72 +14,6 @@ ENUM_VALUE_REGEX: str = r"[a-z]+(_[a-z]+)*" # snake case
|
|
14
14
|
ENUM_VALUE_PATTERN: re.Pattern = re.compile(ENUM_VALUE_REGEX)
|
15
15
|
|
16
16
|
|
17
|
-
class Property():
|
18
|
-
|
19
|
-
__slots__ = ("_name", "_value")
|
20
|
-
|
21
|
-
def __init__(self, name: str, value: Any | PropertyValue) -> None:
|
22
|
-
if not PROPERTY_NAME_PATTERN.fullmatch(name):
|
23
|
-
raise ValueError(f"Invalid property name {name!r}")
|
24
|
-
self._name = name
|
25
|
-
|
26
|
-
if not isinstance(value, PropertyValue):
|
27
|
-
value = PropertyValue.value_factory(value=value)
|
28
|
-
self._value = value
|
29
|
-
|
30
|
-
def __str__(self) -> str:
|
31
|
-
return f"{self._name}={self._value}"
|
32
|
-
|
33
|
-
def __repr__(self) -> str:
|
34
|
-
return (
|
35
|
-
f"{type(self).__name__}("
|
36
|
-
f"name: {self._name}, value: {self._value!r})")
|
37
|
-
|
38
|
-
def __eq__(self, other: Any) -> bool:
|
39
|
-
if not isinstance(other, Property):
|
40
|
-
return NotImplemented
|
41
|
-
return (self.name, self.value) == (other.name, other.value)
|
42
|
-
|
43
|
-
def __lt__(self, other: Any) -> bool:
|
44
|
-
if not isinstance(other, Property):
|
45
|
-
return NotImplemented
|
46
|
-
return (self.name, self.value) < (other.name, other.value)
|
47
|
-
|
48
|
-
@property
|
49
|
-
def name(self) -> str:
|
50
|
-
return self._name
|
51
|
-
|
52
|
-
@property
|
53
|
-
def value(self) -> Any:
|
54
|
-
return self._value.get()
|
55
|
-
|
56
|
-
@value.setter
|
57
|
-
def value(self, value: Any) -> None:
|
58
|
-
self._value.set(value)
|
59
|
-
|
60
|
-
def to_string(self) -> str:
|
61
|
-
return str(self)
|
62
|
-
|
63
|
-
@staticmethod
|
64
|
-
def from_string(string: str, value: str | None = None) -> Property:
|
65
|
-
if value is None:
|
66
|
-
# tread string as "name=value"
|
67
|
-
try:
|
68
|
-
string, value = string.split("=")
|
69
|
-
except ValueError as exc:
|
70
|
-
raise ValueError(f"Invalid property string {string!r}") from exc
|
71
|
-
return Property(name=string, value=PropertyValue.from_string(value))
|
72
|
-
|
73
|
-
def to_nbt(self) -> tuple[str, nbtlib.String]:
|
74
|
-
# return nbtlib.Compound(Name=nbtlib.String(self._name), Value=self._value.to_nbt()})
|
75
|
-
return self._name, self._value.to_nbt()
|
76
|
-
|
77
|
-
@staticmethod
|
78
|
-
def from_nbt(name: str, nbt: nbtlib.String) -> Property:
|
79
|
-
# return Property.from_string(name=nbt["Name"], value=str(nbt["Value"]))
|
80
|
-
return Property.from_string(string=name, value=str(nbt))
|
81
|
-
|
82
|
-
|
83
17
|
class Properties(dict):
|
84
18
|
|
85
19
|
def __init__(self, *args, **kwargs):
|
@@ -160,10 +94,10 @@ class Properties(dict):
|
|
160
94
|
def to_string(self) -> str:
|
161
95
|
return str(self)
|
162
96
|
|
163
|
-
@
|
164
|
-
def from_string(string: str) -> Properties:
|
97
|
+
@classmethod
|
98
|
+
def from_string(cls, string: str) -> Properties:
|
165
99
|
if string in ("", "[]"):
|
166
|
-
return
|
100
|
+
return cls()
|
167
101
|
|
168
102
|
if not (string.startswith("[") and string.endswith("]")):
|
169
103
|
raise ValueError(f"Invalid properties string {string!r}")
|
@@ -179,18 +113,18 @@ class Properties(dict):
|
|
179
113
|
ValueError(f"Duplicate property name {name!r}")
|
180
114
|
props[name] = PropertyValue.from_string(string=val_str).get()
|
181
115
|
|
182
|
-
return
|
116
|
+
return cls(props)
|
183
117
|
|
184
118
|
def to_nbt(self) -> nbtlib.Compound:
|
185
119
|
return nbtlib.Compound(
|
186
120
|
{name: value.to_nbt() for name, value in sorted(super().items())})
|
187
121
|
|
188
|
-
@
|
189
|
-
def from_nbt(nbt: nbtlib.Compound) -> Properties:
|
122
|
+
@classmethod
|
123
|
+
def from_nbt(cls, nbt: nbtlib.Compound) -> Properties:
|
190
124
|
props = {}
|
191
125
|
for name, value in nbt.items():
|
192
126
|
props[name] = PropertyValue.from_nbt(nbt=value).get()
|
193
|
-
return
|
127
|
+
return cls(props)
|
194
128
|
|
195
129
|
|
196
130
|
class PropertyValue(ABC):
|
@@ -270,20 +204,20 @@ class PropertyValue(ABC):
|
|
270
204
|
def to_string(self) -> str:
|
271
205
|
return str(self)
|
272
206
|
|
273
|
-
@
|
274
|
-
def from_string(string: str) -> PropertyValue:
|
207
|
+
@classmethod
|
208
|
+
def from_string(cls, string: str) -> PropertyValue:
|
275
209
|
try:
|
276
210
|
value = json.loads(string)
|
277
211
|
except json.JSONDecodeError:
|
278
212
|
value = string
|
279
|
-
return
|
213
|
+
return cls.value_factory(value)
|
280
214
|
|
281
215
|
def to_nbt(self) -> nbtlib.String:
|
282
216
|
return nbtlib.String(self)
|
283
217
|
|
284
|
-
@
|
285
|
-
def from_nbt(nbt: nbtlib.String) -> PropertyValue:
|
286
|
-
return
|
218
|
+
@classmethod
|
219
|
+
def from_nbt(cls, nbt: nbtlib.String) -> PropertyValue:
|
220
|
+
return cls.from_string(str(nbt))
|
287
221
|
|
288
222
|
|
289
223
|
class BooleanValue(PropertyValue):
|
@@ -4,7 +4,7 @@ from copy import deepcopy
|
|
4
4
|
from nbtlib import Compound
|
5
5
|
from typing import Any, Iterator
|
6
6
|
|
7
|
-
from .resource_location import
|
7
|
+
from .resource_location import BlockId
|
8
8
|
from .block_property import Properties
|
9
9
|
|
10
10
|
|
@@ -12,8 +12,15 @@ class BlockState:
|
|
12
12
|
|
13
13
|
__slots__ = ("_id", "_props")
|
14
14
|
|
15
|
-
def __init__(self, _id: str, **props: Any) -> None:
|
16
|
-
|
15
|
+
def __init__(self, _id: str | BlockId, **props: Any) -> None:
|
16
|
+
if isinstance(_id, str):
|
17
|
+
_id = BlockId.from_string(_id)
|
18
|
+
elif not isinstance(_id, BlockId):
|
19
|
+
raise TypeError(
|
20
|
+
f"'_id' has to be str or {BlockId.__name__}, got"
|
21
|
+
f" {type(_id).__name__}")
|
22
|
+
self._id: BlockId = _id
|
23
|
+
|
17
24
|
self._props: Properties = Properties(**props)
|
18
25
|
|
19
26
|
def __getitem__(self, name: str) -> Any:
|
@@ -58,8 +65,8 @@ class BlockState:
|
|
58
65
|
f"id: {self._id!r}, props: {self._props!r})")
|
59
66
|
|
60
67
|
@property
|
61
|
-
def id(self) ->
|
62
|
-
return
|
68
|
+
def id(self) -> BlockId:
|
69
|
+
return self._id
|
63
70
|
|
64
71
|
def props(self) -> Iterator[tuple[str, Any]]:
|
65
72
|
return self._props.items()
|
@@ -67,15 +74,15 @@ class BlockState:
|
|
67
74
|
def to_string(self) -> str:
|
68
75
|
return str(self)
|
69
76
|
|
70
|
-
@
|
71
|
-
def from_string(string: str) -> BlockState:
|
77
|
+
@classmethod
|
78
|
+
def from_string(cls, string: str) -> BlockState:
|
72
79
|
idx = string.find("[") # basic parsing to separate block:id[name=value]
|
73
80
|
if idx == -1:
|
74
81
|
id, props = string, ""
|
75
82
|
else:
|
76
83
|
id, props = string[:idx], string[idx:]
|
77
84
|
|
78
|
-
state =
|
85
|
+
state = cls(id)
|
79
86
|
state._props = Properties.from_string(props)
|
80
87
|
return state
|
81
88
|
|
@@ -86,19 +93,19 @@ class BlockState:
|
|
86
93
|
nbt["Properties"] = self._props.to_nbt()
|
87
94
|
return nbt
|
88
95
|
|
89
|
-
@
|
90
|
-
def from_nbt(nbt: Compound) -> BlockState:
|
91
|
-
state =
|
96
|
+
@classmethod
|
97
|
+
def from_nbt(cls, nbt: Compound) -> BlockState:
|
98
|
+
state = cls(str(nbt["Name"]))
|
92
99
|
state._props = Properties.from_nbt(nbt.get("Properties", Compound()))
|
93
100
|
return state
|
94
101
|
|
95
102
|
def with_id(self, id: str) -> BlockState:
|
96
|
-
state =
|
103
|
+
state = type(self)(id)
|
97
104
|
state._props = deepcopy(self._props)
|
98
105
|
return state
|
99
106
|
|
100
107
|
def with_props(self, **props: Any) -> BlockState:
|
101
|
-
state =
|
108
|
+
state = type(self)(self.id)
|
102
109
|
new_props = deepcopy(self._props)
|
103
110
|
for name, value in props.items():
|
104
111
|
if value is None:
|
@@ -0,0 +1,171 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
import enum
|
5
|
+
import nbtlib
|
6
|
+
import numpy as np
|
7
|
+
from typing import Iterator
|
8
|
+
|
9
|
+
|
10
|
+
@dataclass(frozen=True)
|
11
|
+
class Vec3i:
|
12
|
+
_a: int
|
13
|
+
_b: int
|
14
|
+
_c: int
|
15
|
+
|
16
|
+
def __post_init__(self) -> None:
|
17
|
+
object.__setattr__(self, "_a", int(self._a))
|
18
|
+
object.__setattr__(self, "_b", int(self._b))
|
19
|
+
object.__setattr__(self, "_c", int(self._c))
|
20
|
+
|
21
|
+
def __getitem__(self, index: int) -> int:
|
22
|
+
return tuple(self)[index]
|
23
|
+
|
24
|
+
def __str__(self) -> str:
|
25
|
+
return str(list(self))
|
26
|
+
|
27
|
+
def __repr__(self) -> str:
|
28
|
+
return (
|
29
|
+
f"{type(self).__name__}(a={self._a}, b={self._b}, c={self._c})")
|
30
|
+
|
31
|
+
def __len__(self) -> int:
|
32
|
+
return 3
|
33
|
+
|
34
|
+
def __iter__(self) -> Iterator[int]:
|
35
|
+
return iter((self._a, self._b, self._c))
|
36
|
+
|
37
|
+
def __neg__(self) -> Vec3i:
|
38
|
+
return type(self)(*(-i for i in self))
|
39
|
+
|
40
|
+
def __abs__(self) -> Vec3i:
|
41
|
+
return type(self)(*(abs(i) for i in self))
|
42
|
+
|
43
|
+
def __array__(self, dtype: type | None = None, copy: bool = True):
|
44
|
+
arr = np.array(tuple(self), dtype=dtype)
|
45
|
+
return arr.copy() if copy else arr
|
46
|
+
|
47
|
+
def __add__(self, other) -> Vec3i:
|
48
|
+
return type(self)(*(np.array(self) + other))
|
49
|
+
|
50
|
+
def __radd__(self, other) -> Vec3i:
|
51
|
+
return self.__add__(other)
|
52
|
+
|
53
|
+
def __sub__(self, other) -> Vec3i:
|
54
|
+
return self.__add__(-other)
|
55
|
+
|
56
|
+
def __rsub__(self, other) -> Vec3i:
|
57
|
+
return -self.__sub__(other)
|
58
|
+
|
59
|
+
def __mul__(self, other) -> Vec3i:
|
60
|
+
return type(self)(*(np.array(self) * other))
|
61
|
+
|
62
|
+
def __rmul__(self, other) -> Vec3i:
|
63
|
+
return self.__mul__(other)
|
64
|
+
|
65
|
+
def __floordiv__(self, other) -> Vec3i:
|
66
|
+
return type(self)(*(np.array(self) // other))
|
67
|
+
|
68
|
+
def __rfloordiv__(self, other) -> Vec3i:
|
69
|
+
return type(self)(*(other // np.array(self)))
|
70
|
+
|
71
|
+
def __truediv__(self, other) -> Vec3i:
|
72
|
+
return self.__floordiv__(other)
|
73
|
+
|
74
|
+
def __rtruediv__(self, other) -> Vec3i:
|
75
|
+
return self.__rfloordiv__(other)
|
76
|
+
|
77
|
+
def __mod__(self, other) -> Vec3i:
|
78
|
+
return type(self)(*(np.array(self) % other))
|
79
|
+
|
80
|
+
def __rmod__(self, other) -> Vec3i:
|
81
|
+
return type(self)(*(other % np.array(self)))
|
82
|
+
|
83
|
+
def __eq__(self, other):
|
84
|
+
return np.array(self) == other
|
85
|
+
|
86
|
+
def __ne__(self, other):
|
87
|
+
return np.invert(self.__eq__(other))
|
88
|
+
|
89
|
+
def __lt__(self, other):
|
90
|
+
return np.array(self) < other
|
91
|
+
|
92
|
+
def __le__(self, other):
|
93
|
+
return np.array(self) <= other
|
94
|
+
|
95
|
+
def __gt__(self, other):
|
96
|
+
return np.array(self) > other
|
97
|
+
|
98
|
+
def __ge__(self, other):
|
99
|
+
return np.array(self) >= other
|
100
|
+
|
101
|
+
def to_nbt(self) -> nbtlib.Compound:
|
102
|
+
return nbtlib.Compound({
|
103
|
+
"x": nbtlib.Int(self._a),
|
104
|
+
"y": nbtlib.Int(self._b),
|
105
|
+
"z": nbtlib.Int(self._c),
|
106
|
+
})
|
107
|
+
|
108
|
+
@classmethod
|
109
|
+
def from_nbt(cls, nbt: nbtlib.Compound) -> Vec3i:
|
110
|
+
return cls(int(nbt["x"]), int(nbt["y"]), int(nbt["z"]))
|
111
|
+
|
112
|
+
|
113
|
+
@dataclass(frozen=True)
|
114
|
+
class BlockPosition(Vec3i):
|
115
|
+
|
116
|
+
@property
|
117
|
+
def x(self) -> int:
|
118
|
+
return self._a
|
119
|
+
|
120
|
+
@property
|
121
|
+
def y(self) -> int:
|
122
|
+
return self._b
|
123
|
+
|
124
|
+
@property
|
125
|
+
def z(self) -> int:
|
126
|
+
return self._c
|
127
|
+
|
128
|
+
def __repr__(self) -> str:
|
129
|
+
return f"{type(self).__name__}(x={self.x}, y={self.y}, z={self.z})"
|
130
|
+
|
131
|
+
|
132
|
+
class Direction(BlockPosition, enum.Enum):
|
133
|
+
NORTH = 0, 0, -1
|
134
|
+
SOUTH = 0, 0, 1
|
135
|
+
WEST = -1, 0, 0
|
136
|
+
EAST = 1, 0, 0
|
137
|
+
UP = 0, 1, 0
|
138
|
+
DOWN = 0, -1, 0
|
139
|
+
|
140
|
+
|
141
|
+
@dataclass(frozen=True)
|
142
|
+
class Size3D(Vec3i):
|
143
|
+
|
144
|
+
@property
|
145
|
+
def width(self) -> int:
|
146
|
+
return self._a
|
147
|
+
|
148
|
+
@property
|
149
|
+
def height(self) -> int:
|
150
|
+
return self._b
|
151
|
+
|
152
|
+
@property
|
153
|
+
def length(self) -> int:
|
154
|
+
return self._c
|
155
|
+
|
156
|
+
def __repr__(self) -> str:
|
157
|
+
return (
|
158
|
+
f"{type(self).__name__}("
|
159
|
+
f"width={self.width}, height={self.height}, length={self.length})")
|
160
|
+
|
161
|
+
@property
|
162
|
+
def volume(self) -> int:
|
163
|
+
return abs(self.width * self.height * self.length)
|
164
|
+
|
165
|
+
@property
|
166
|
+
def limit(self) -> BlockPosition:
|
167
|
+
return BlockPosition(*(self - self.sign))
|
168
|
+
|
169
|
+
@property
|
170
|
+
def sign(self) -> BlockPosition:
|
171
|
+
return BlockPosition(*np.sign(self))
|