pylitematic 0.0.2__py3-none-any.whl → 0.0.4__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.
pylitematic/__init__.py CHANGED
@@ -1,7 +1,7 @@
1
- __version__ = "0.0.2"
1
+ __version__ = "0.0.4"
2
2
 
3
3
  from .block_state import BlockState
4
- from .geometry import BlockPosition, Size3D
4
+ from .geometry import BlockPosition, Direction, Size3D
5
5
  from .region import Region
6
- from .resource_location import ResourceLocation
6
+ from .resource_location import BlockId
7
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:
pylitematic/geometry.py CHANGED
@@ -1,8 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
+ import enum
4
5
  import nbtlib
5
6
  import numpy as np
7
+ from typing import Iterator
6
8
 
7
9
 
8
10
  @dataclass(frozen=True)
@@ -12,134 +14,89 @@ class Vec3i:
12
14
  _c: int
13
15
 
14
16
  def __post_init__(self) -> None:
15
- object.__setattr__(self, "_a", self._to_int(self._a))
16
- object.__setattr__(self, "_b", self._to_int(self._b))
17
- object.__setattr__(self, "_c", self._to_int(self._c))
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]
18
23
 
19
24
  def __str__(self) -> str:
20
25
  return str(list(self))
21
26
 
27
+ def __repr__(self) -> str:
28
+ return (
29
+ f"{type(self).__name__}(a={self._a}, b={self._b}, c={self._c})")
30
+
22
31
  def __len__(self) -> int:
23
32
  return 3
24
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
+
25
47
  def __add__(self, other) -> Vec3i:
26
- arr = np.array(self)
27
- other = self._to_array(other)
28
- try:
29
- result = arr + other
30
- except Exception:
31
- return NotImplemented
32
- return type(self)(*result.astype(int))
48
+ return type(self)(*(np.array(self) + other))
33
49
 
34
50
  def __radd__(self, other) -> Vec3i:
35
51
  return self.__add__(other)
36
52
 
37
53
  def __sub__(self, other) -> Vec3i:
38
- arr = np.array(self)
39
- other = self._to_array(other)
40
- try:
41
- result = arr - other
42
- except Exception:
43
- return NotImplemented
44
- return type(self)(*result.astype(int))
54
+ return self.__add__(-other)
45
55
 
46
56
  def __rsub__(self, other) -> Vec3i:
47
- arr = np.array(self)
48
- try:
49
- result = other - arr
50
- except Exception:
51
- return NotImplemented
52
- return type(self)(*result.astype(int))
57
+ return -self.__sub__(other)
53
58
 
54
59
  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))
60
+ return type(self)(*(np.array(self) * other))
62
61
 
63
62
  def __rmul__(self, other) -> Vec3i:
64
63
  return self.__mul__(other)
65
64
 
66
65
  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))
66
+ return type(self)(*(np.array(self) // other))
74
67
 
75
68
  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))
69
+ return type(self)(*(other // np.array(self)))
82
70
 
83
- def __neg__(self) -> Vec3i:
84
- return type(self)(-self._a, -self._b, -self._c)
71
+ def __truediv__(self, other) -> Vec3i:
72
+ return self.__floordiv__(other)
85
73
 
86
- def __getitem__(self, index: int) -> int:
87
- return self.to_tuple()[index]
74
+ def __rtruediv__(self, other) -> Vec3i:
75
+ return self.__rfloordiv__(other)
88
76
 
89
- def __iter__(self):
90
- return iter(self.to_tuple())
77
+ def __mod__(self, other) -> Vec3i:
78
+ return type(self)(*(np.array(self) % other))
91
79
 
92
- def __abs__(self) -> Vec3i:
93
- return type(self)(*np.abs(self))
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))
94
88
 
95
89
  def __lt__(self, other):
96
- return np.array(self) < self._to_array(other)
90
+ return np.array(self) < other
97
91
 
98
92
  def __le__(self, other):
99
- return np.array(self) <= self._to_array(other)
93
+ return np.array(self) <= other
100
94
 
101
95
  def __gt__(self, other):
102
- return np.array(self) > self._to_array(other)
96
+ return np.array(self) > other
103
97
 
104
98
  def __ge__(self, other):
105
- return np.array(self) >= self._to_array(other)
106
-
107
- def __eq__(self, other):
108
- return np.array(self) == self._to_array(other)
109
-
110
- def __ne__(self, other):
111
- return np.array(self) != self._to_array(other)
112
-
113
- def __array__(self, dtype: type | None = None, copy: bool = True):
114
- arr = np.array([self._a, self._b, self._c], dtype=dtype)
115
- if copy:
116
- return arr.copy()
117
- else:
118
- return arr
119
-
120
- def _to_array(self, other):
121
- if isinstance(other, Vec3i):
122
- return np.array(other)
123
- else:
124
- return other
125
-
126
- @staticmethod
127
- def _to_int(value) -> int:
128
- if isinstance(value, (int, np.integer)):
129
- return int(value)
130
- elif isinstance(value, float):
131
- if value.is_integer():
132
- return int(value)
133
- raise TypeError(
134
- f"{type(value).__name__} value {value!r} is not"
135
- " int, numpy integer, or whole float")
136
-
137
- def to_tuple(self) -> tuple[int, int, int]:
138
- return (self._a, self._b, self._c)
139
-
140
- @classmethod
141
- def from_tuple(cls, t: tuple[int, int, int]) -> Vec3i:
142
- return cls(*t)
99
+ return np.array(self) >= other
143
100
 
144
101
  def to_nbt(self) -> nbtlib.Compound:
145
102
  return nbtlib.Compound({
@@ -172,6 +129,15 @@ class BlockPosition(Vec3i):
172
129
  return f"{type(self).__name__}(x={self.x}, y={self.y}, z={self.z})"
173
130
 
174
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
+
175
141
  @dataclass(frozen=True)
176
142
  class Size3D(Vec3i):
177
143
 
@@ -191,3 +157,15 @@ class Size3D(Vec3i):
191
157
  return (
192
158
  f"{type(self).__name__}("
193
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))