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.
Files changed (24) hide show
  1. {pylitematic-0.0.2 → pylitematic-0.0.4}/PKG-INFO +7 -1
  2. {pylitematic-0.0.2 → pylitematic-0.0.4}/pyproject.toml +11 -2
  3. pylitematic-0.0.4/src/pylitematic/__init__.py +7 -0
  4. {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic/block_property.py +13 -79
  5. {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic/block_state.py +20 -13
  6. pylitematic-0.0.4/src/pylitematic/geometry.py +171 -0
  7. pylitematic-0.0.4/src/pylitematic/region.py +485 -0
  8. {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic/resource_location.py +10 -6
  9. {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic/schematic.py +18 -13
  10. pylitematic-0.0.4/src/pylitematic/test.py +72 -0
  11. {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic.egg-info/PKG-INFO +7 -1
  12. {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic.egg-info/SOURCES.txt +2 -1
  13. {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic.egg-info/requires.txt +4 -0
  14. {pylitematic-0.0.2 → pylitematic-0.0.4}/tests/test_block_property.py +0 -42
  15. pylitematic-0.0.4/tests/test_schematic.py +40 -0
  16. pylitematic-0.0.2/src/pylitematic/__init__.py +0 -7
  17. pylitematic-0.0.2/src/pylitematic/geometry.py +0 -193
  18. pylitematic-0.0.2/src/pylitematic/region.py +0 -328
  19. pylitematic-0.0.2/src/pylitematic/test.py +0 -39
  20. {pylitematic-0.0.2 → pylitematic-0.0.4}/README.md +0 -0
  21. {pylitematic-0.0.2 → pylitematic-0.0.4}/setup.cfg +0 -0
  22. {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic.egg-info/dependency_links.txt +0 -0
  23. {pylitematic-0.0.2 → pylitematic-0.0.4}/src/pylitematic.egg-info/top_level.txt +0 -0
  24. {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.2
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.2"
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]
@@ -0,0 +1,7 @@
1
+ __version__ = "0.0.4"
2
+
3
+ from .block_state import BlockState
4
+ from .geometry import BlockPosition, Direction, Size3D
5
+ from .region import Region
6
+ from .resource_location import BlockId
7
+ from .schematic import Schematic
@@ -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
- @staticmethod
164
- def from_string(string: str) -> Properties:
97
+ @classmethod
98
+ def from_string(cls, string: str) -> Properties:
165
99
  if string in ("", "[]"):
166
- return Properties()
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 Properties(props)
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
- @staticmethod
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 Properties(props)
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
- @staticmethod
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 PropertyValue.value_factory(value)
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
- @staticmethod
285
- def from_nbt(nbt: nbtlib.String) -> PropertyValue:
286
- return PropertyValue.from_string(str(nbt))
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 ResourceLocation
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
- self._id: ResourceLocation = ResourceLocation.from_string(_id)
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) -> str:
62
- return str(self._id)
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
- @staticmethod
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 = BlockState(id)
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
- @staticmethod
90
- def from_nbt(nbt: Compound) -> BlockState:
91
- state = BlockState(str(nbt["Name"]))
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 = BlockState(id)
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 = BlockState(self.id)
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))