Skip to content

Commit 8556a1e

Browse files
committed
Some tweaks to #103 and updated changelog
1 parent 4931bb6 commit 8556a1e

File tree

2 files changed

+68
-49
lines changed

2 files changed

+68
-49
lines changed

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@
22

33
Compatible with GDMC-HTTP **>=1.0.0, <2.0.0** and Minecraft **1.20.2**.
44

5+
**Additions:**
6+
- Added various ordered vector list constants to `vector_tools`. (Thanks [Flashing-Blinkenlights](https://github.com/Flashing-Blinkenlights)!)
7+
- Added `utils.rotateSequence`. (Thanks [Flashing-Blinkenlights](https://github.com/Flashing-Blinkenlights)!)
8+
9+
**Fixes:**
10+
- Made some previously mutable constants immutable. (Thanks [Flashing-Blinkenlights](https://github.com/Flashing-Blinkenlights)!)
11+
512

613
# 7.2.0
714

815
Compatible with GDMC-HTTP **>=1.0.0, <2.0.0** and Minecraft **1.20.2**.
916

10-
**Additions**
17+
**Additions:**
1118
- Added `vector_tools.rotate2Ddeg` and `rotate3Ddeg`. (Thanks [Flashing-Blinkenlights](https://github.com/Flashing-Blinkenlights)!)
1219
- Added many new constants to `vector_tools`. (Thanks [Flashing-Blinkenlights](https://github.com/Flashing-Blinkenlights)!)
1320

src/gdpc/vector_tools.py

Lines changed: 60 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,13 @@ def __iter__(self) -> Iterator[bool]: ...
6868
# Constants
6969
# ==================================================================================================
7070

71+
7172
# ==== 2D values ====
7273

74+
7375
# == constants ==
7476

77+
7578
X_2D = ivec2(1, 0)
7679
Y_2D = ivec2(0, 1)
7780
XY_2D: ivec2 = X_2D + Y_2D
@@ -97,50 +100,53 @@ def __iter__(self) -> Iterator[bool]: ...
97100
ORDERED_INTERCARDINALS_2D: Tuple[ivec2, ...] = (SOUTHEAST_2D, SOUTHWEST_2D, NORTHWEST_2D, NORTHEAST_2D)
98101
ORDERED_CARDINALS_AND_DIAGONALS_2D: Tuple[ivec2, ...] = tuple(itertools.chain.from_iterable(zip(ORDERED_CARDINALS_2D, ORDERED_INTERCARDINALS_2D)))
99102

103+
100104
# ==== 3D values ====
101105

106+
102107
# == functions for generating constants ==
103-
def _generateSpiraloidDirectionVector3D(
108+
109+
110+
def _spiraloidDirections3D(
104111
top_pattern: Optional[Tuple[ivec3, ...]],
105-
central_pattern: Optional[Tuple[ivec3, ...]],
106-
base_pattern: Optional[Tuple[ivec3, ...]],
112+
center_pattern: Optional[Tuple[ivec3, ...]],
113+
bottom_pattern: Optional[Tuple[ivec3, ...]],
107114
include_up: bool = False,
108115
include_center: bool = False,
109116
include_down: bool = False
110-
) -> Tuple[ivec3, ...]:
111-
"""Generate a set of 3D direction vectors, where patterns are provided to be combined with a top, neutral and bottom vector."""
112-
113-
# Becomes a tuple of ivec3 or None. If desired, consists of...
114-
# NOTE: The brackets and the use of None/() are particular to the way unpacking is handled
115-
template_tuple: Tuple[ivec3 | None, ...] = (
116-
UP_3D if include_up else None, # ...the UP vector...
117-
*([UP_3D + c for c in top_pattern] if top_pattern else ()), # ...the upward diagonal vectors...
118-
*(central_pattern[:len(central_pattern)//2] if central_pattern else ()), # ...the first half of the horizontal vectors...
119-
ivec3(0, 0, 0) if include_center else None, # ...the origin...
120-
*(central_pattern[len(central_pattern)//2:] if central_pattern else ()), # ...the second half of the horizontal vectors...
121-
*([DOWN_3D + c for c in base_pattern] if base_pattern else ()), # ...the downward diagonal vectors...
122-
DOWN_3D if include_down else None # ...and the DOWN vector.
123-
)
124-
return tuple(e for e in template_tuple if e) # Get rid of the Nones, return the rest
125-
126-
def _generateSymmetricSpiraloidVectors3D(
127-
top_and_base_pattern: Optional[Tuple[ivec3, ...]],
128-
central_pattern: Optional[Tuple[ivec3, ...]],
129-
include_up_and_down: bool = False,
130-
include_center: bool = False,
131-
) -> Tuple[ivec3, ...]:
132-
"""Generate a set of 3D direction vectors, mirrored along the XY plane."""
133-
return _generateSpiraloidDirectionVector3D(
134-
top_pattern = top_and_base_pattern,
135-
central_pattern = central_pattern,
136-
base_pattern = top_and_base_pattern,
117+
) -> Generator[ivec3, None, None]:
118+
"""yields 3D direction vectors of a spiraloid, where patterns can be provided to be combined with a top, center and bottom vector."""
119+
120+
# If desired, yields...
121+
if include_up: yield UP_3D # ...the UP vector...
122+
if top_pattern: yield from (UP_3D + c for c in top_pattern) # ...the upward diagonal vectors...
123+
if center_pattern: yield from center_pattern[:len(center_pattern)//2] # ...the first half of the horizontal vectors...
124+
if include_center: yield ivec3(0, 0, 0) # ...the origin...
125+
if center_pattern: yield from center_pattern[len(center_pattern)//2:] # ...the second half of the horizontal vectors...
126+
if bottom_pattern: yield from (DOWN_3D + c for c in bottom_pattern) # ...the downward diagonal vectors...
127+
if include_down: yield DOWN_3D # ...and the DOWN vector.
128+
129+
130+
def _symmetricSpiraloidDirections3D(
131+
top_and_bottom_pattern: Optional[Tuple[ivec3, ...]],
132+
central_pattern: Optional[Tuple[ivec3, ...]],
133+
include_up_and_down: bool = False,
134+
include_center: bool = False,
135+
) -> Generator[ivec3, None, None]:
136+
"""Yields 3D direction vectors of a spiraloid, mirrored across the XZ-plane."""
137+
yield from _spiraloidDirections3D(
138+
top_pattern = top_and_bottom_pattern,
139+
center_pattern = central_pattern,
140+
bottom_pattern = top_and_bottom_pattern,
137141
include_up = include_up_and_down,
138142
include_center = include_center,
139143
include_down = include_up_and_down
140144
)
141145

146+
142147
# == constants ==
143148

149+
144150
X_3D = ivec3(1, 0, 0)
145151
Y_3D = ivec3(0, 1, 0)
146152
Z_3D = ivec3(0, 0, 1)
@@ -170,7 +176,7 @@ def _generateSymmetricSpiraloidVectors3D(
170176
# starting East, moving clockwise
171177
# NOTE: Use `utils.rotateSequence(ORDERED_..., n)` to change the starting point while maintaining the order of the sequence
172178
# E.g. n=1 starts at the second point; n=-1 starts at the last point
173-
# NOTE: Use `reverse(utils.rotateSequence(ORDERED_...))` to move counterclockwise from east
179+
# NOTE: Use `reversed(utils.rotateSequence(ORDERED_...))` to move counterclockwise from east
174180
# This does not work for sequences with differing Y-values!
175181
# To achieve that, transform the values for each layer first, then recombine them.
176182
# E.g. `reverse(utils.rotateSequence([UP_3D + c for c in ORDERED_CARDINALS_3D])) + reverse(utils.rotateSequence(ORDERED_CARDINALS_3D)) + ...`
@@ -195,13 +201,16 @@ def _generateSymmetricSpiraloidVectors3D(
195201
# Moving Up to Down, clockwise starting East
196202
# NOTE: For other combinations, use `generate_[symmetric_]spiraloid_vectors_3D()`
197203
ORDERED_DIRECTIONS_3D: Tuple[ivec3, ...] = (UP_3D, *ORDERED_CARDINALS_3D, DOWN_3D)
198-
ORDERED_EDGE_DIAGONALS_3D: Tuple[ivec3, ...] = _generateSymmetricSpiraloidVectors3D(ORDERED_CARDINALS_3D, ORDERED_INTERCARDINALS_3D )
199-
ORDERED_DIRECTIONS_AND_EDGE_DIAGONALS_3D: Tuple[ivec3, ...] = _generateSymmetricSpiraloidVectors3D(ORDERED_CARDINALS_3D, ORDERED_CARDINALS_AND_DIAGONALS_3D, include_up_and_down=True)
200-
ORDERED_CORNER_DIAGONALS_3D: Tuple[ivec3, ...] = _generateSymmetricSpiraloidVectors3D(ORDERED_INTERCARDINALS_3D, None )
201-
ORDERED_DIRECTIONS_AND_ALL_DIAGONALS_3D: Tuple[ivec3, ...] = _generateSymmetricSpiraloidVectors3D(ORDERED_CARDINALS_AND_DIAGONALS_3D, ORDERED_CARDINALS_AND_DIAGONALS_3D, include_up_and_down=True)
202-
ORDERED_DIAGONALS: Tuple[ivec3, ...] = _generateSymmetricSpiraloidVectors3D(ORDERED_CARDINALS_AND_DIAGONALS_3D, ORDERED_INTERCARDINALS_3D )
204+
ORDERED_EDGE_DIAGONALS_3D: Tuple[ivec3, ...] = tuple(_symmetricSpiraloidDirections3D(ORDERED_CARDINALS_3D, ORDERED_INTERCARDINALS_3D ))
205+
ORDERED_DIRECTIONS_AND_EDGE_DIAGONALS_3D: Tuple[ivec3, ...] = tuple(_symmetricSpiraloidDirections3D(ORDERED_CARDINALS_3D, ORDERED_CARDINALS_AND_DIAGONALS_3D, include_up_and_down=True))
206+
ORDERED_CORNER_DIAGONALS_3D: Tuple[ivec3, ...] = tuple(_symmetricSpiraloidDirections3D(ORDERED_INTERCARDINALS_3D, None ))
207+
ORDERED_DIRECTIONS_AND_ALL_DIAGONALS_3D: Tuple[ivec3, ...] = tuple(_symmetricSpiraloidDirections3D(ORDERED_CARDINALS_AND_DIAGONALS_3D, ORDERED_CARDINALS_AND_DIAGONALS_3D, include_up_and_down=True))
208+
ORDERED_DIAGONALS: Tuple[ivec3, ...] = tuple(_symmetricSpiraloidDirections3D(ORDERED_CARDINALS_AND_DIAGONALS_3D, ORDERED_INTERCARDINALS_3D ))
209+
203210

204211
# ==== aliases ====
212+
213+
205214
X: ivec3 = X_3D
206215
Y: ivec3 = Y_3D
207216
Z: ivec3 = Z_3D
@@ -250,34 +259,41 @@ def dropDimension(vec: Vec3iLike, dimension: int) -> ivec2:
250259
if dimension == 2: return ivec2(vec[0], vec[1])
251260
raise ValueError(f'Invalid dimension "{dimension}"')
252261

262+
253263
def addDimension(vec: Vec2iLike, dimension: int, value: int = 0) -> ivec3:
254264
"""Inserts <value> into <vec> at <dimension> and returns the resulting 3D vector"""
255265
# NOTE: Should be adjusted to only support 2D -> 3D, or all ivec dimensions
256266
l = list(vec)
257267
return ivec3(*l[:dimension], value, *l[dimension:])
258268

269+
259270
def dropY(vec: Vec3iLike) -> ivec2:
260271
"""Returns [vec] without its y-component (i.e., projected on the XZ-plane)"""
261272
return ivec2(vec[0], vec[2])
262273

274+
263275
def addY(vec: Vec2iLike, y=0) -> ivec3:
264276
"""Returns a 3D vector (vec[0], y, vec[1])"""
265277
return ivec3(vec[0], y, vec[1])
266278

279+
267280
def setY(vec: Vec3iLike, y=0) -> ivec3:
268281
"""Returns [vec] with its y-component set to [y]"""
269282
return ivec3(vec[0], y, vec[2])
270283

284+
271285
def trueMod2D(vec: Vec2iLike, modulus: int) -> ivec2:
272286
"""Returns <v> modulo <modulus>.\n
273287
Negative numbers are handled just like Python's built-in integer modulo."""
274288
return ivec2(vec[0] % modulus, vec[1] % modulus)
275289

290+
276291
def trueMod3D(vec: Vec3iLike, modulus: int) -> ivec3:
277292
"""Returns <v> modulo <modulus>.\n
278293
Negative numbers are handled just like Python's built-in integer modulo."""
279294
return ivec3(vec[0] % modulus, vec[1] % modulus, vec[2] % modulus)
280295

296+
281297
def perpendicular(vec: Vec2iLike) -> ivec2:
282298
"""Returns the vector perpendicular to [vec] that points to the right of [vec] and has the same
283299
length as [vec]."""
@@ -357,11 +373,13 @@ def flipRotation3D(rotation: int, flip: Vec3bLike) -> int:
357373
after <rotation>"""
358374
return flipRotation2D(rotation, dropY(flip))
359375

376+
360377
def rotateSize2D(size: Vec2iLike, rotation: int) -> Vec2iLike:
361378
"""Returns the effective size of a rect of size [size] that has been rotated in the XZ-plane by
362379
[rotation]."""
363380
return ivec2(size[1], size[0]) if rotation in {1, 3} else size
364381

382+
365383
def rotateSize3D(size: Vec3iLike, rotation: int) -> ivec3:
366384
"""Returns the effective size of a box of size [size] that has been rotated in the XZ-plane by
367385
[rotation]."""
@@ -1481,7 +1499,7 @@ def fittingSphere(corner1: Vec3iLike, corner2: Vec3iLike, hollow: bool = False)
14811499
return sphere(center, diameter, hollow)
14821500

14831501

1484-
def _inboundNeighborsFromVectors2D(point: ivec2, bounding_rect: Rect, vectors: Iterable[ivec2], stride: int = 1) -> Generator[ivec2, Any, None]:
1502+
def _boundedNeighborsFromVectors2D(point: ivec2, bounding_rect: Rect, vectors: Iterable[ivec2], stride: int = 1) -> Generator[ivec2, Any, None]:
14851503
"""Generate neighboring vectors within a bounding rect in the directions of vectors."""
14861504
for vector in vectors:
14871505
candidate: ivec2 = point + stride * vector
@@ -1492,15 +1510,12 @@ def _inboundNeighborsFromVectors2D(point: ivec2, bounding_rect: Rect, vectors: I
14921510
def neighbors2D(point: Vec2iLike, boundingRect: Rect, diagonal: bool = False, stride: int = 1) -> Generator[ivec2, Any, None]:
14931511
"""Yields the neighbors of [point] within [bounding_rect].\n
14941512
Useful for pathfinding."""
1495-
1496-
if type(point) is not ivec2:
1497-
point = ivec2(*point)
1498-
1513+
point = ivec2(*point)
14991514
vectors: FrozenSet[ivec2] = CARDINALS_AND_DIAGONALS_2D if diagonal else CARDINALS_2D
1500-
return _inboundNeighborsFromVectors2D(point, boundingRect, vectors, stride)
1515+
return _boundedNeighborsFromVectors2D(point, boundingRect, vectors, stride)
15011516

15021517

1503-
def _inboundNeighborsFromVectors3D(point: ivec3, bounding_box: Box, vectors: Iterable[ivec3], stride: int = 1) -> Generator[ivec3, Any, None]:
1518+
def _boundedNeighborsFromVectors3D(point: ivec3, bounding_box: Box, vectors: Iterable[ivec3], stride: int = 1) -> Generator[ivec3, Any, None]:
15041519
"""Generate neighboring vectors within a bounding box in the directions of vectors."""
15051520
for vector in vectors:
15061521
candidate: ivec3 = point + stride * vector
@@ -1511,9 +1526,6 @@ def _inboundNeighborsFromVectors3D(point: ivec3, bounding_box: Box, vectors: Ite
15111526
def neighbors3D(point: Vec3iLike, boundingBox: Box, diagonal: bool = False, stride: int = 1) -> Generator[ivec3, Any, None]:
15121527
"""Yields the neighbors of [point] within [bounding_box].\n
15131528
Useful for pathfinding."""
1514-
1515-
if type(point) is not ivec3:
1516-
point = ivec3(*point)
1517-
1529+
point = ivec3(*point)
15181530
vectors: FrozenSet[ivec3] = DIRECTIONS_AND_ALL_DIAGONALS_3D if diagonal else DIRECTIONS_3D
1519-
return _inboundNeighborsFromVectors3D(point, boundingBox, vectors, stride)
1531+
return _boundedNeighborsFromVectors3D(point, boundingBox, vectors, stride)

0 commit comments

Comments
 (0)