@@ -68,10 +68,13 @@ def __iter__(self) -> Iterator[bool]: ...
6868# Constants
6969# ==================================================================================================
7070
71+
7172# ==== 2D values ====
7273
74+
7375# == constants ==
7476
77+
7578X_2D = ivec2 (1 , 0 )
7679Y_2D = ivec2 (0 , 1 )
7780XY_2D : ivec2 = X_2D + Y_2D
@@ -97,50 +100,53 @@ def __iter__(self) -> Iterator[bool]: ...
97100ORDERED_INTERCARDINALS_2D : Tuple [ivec2 , ...] = (SOUTHEAST_2D , SOUTHWEST_2D , NORTHWEST_2D , NORTHEAST_2D )
98101ORDERED_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+
144150X_3D = ivec3 (1 , 0 , 0 )
145151Y_3D = ivec3 (0 , 1 , 0 )
146152Z_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()`
197203ORDERED_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+
205214X : ivec3 = X_3D
206215Y : ivec3 = Y_3D
207216Z : 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+
253263def 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+
259270def 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+
263275def 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+
267280def 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+
271285def 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+
276291def 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+
281297def 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+
360377def 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+
365383def 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
14921510def 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
15111526def 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