welcome to the 3D world
This commit is contained in:
parent
0c0a9a7eb1
commit
91e2477328
102
coordinate.py
102
coordinate.py
@ -1,68 +1,110 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum
|
||||||
from math import sqrt, inf, atan2, degrees
|
from math import sqrt, inf, atan2, degrees
|
||||||
from typing import Union, List
|
from typing import Union, List, Optional
|
||||||
|
|
||||||
|
|
||||||
|
class DistanceAlgorithm(Enum):
|
||||||
|
MANHATTAN = 0
|
||||||
|
PYTHAGORAS = 1
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, order=True)
|
@dataclass(frozen=True, order=True)
|
||||||
class Coordinate:
|
class Coordinate:
|
||||||
x: int
|
x: int
|
||||||
y: int
|
y: int
|
||||||
|
z: Optional[int] = None
|
||||||
|
|
||||||
def getDistanceTo(self, target: Coordinate, mode: int = 1, includeDiagonals: bool = False) -> Union[int, float]:
|
def getDistanceTo(self, target: Coordinate, mode: DistanceAlgorithm = DistanceAlgorithm.PYTHAGORAS,
|
||||||
|
includeDiagonals: bool = False) -> Union[int, float]:
|
||||||
"""
|
"""
|
||||||
Get distance to target Coordinate
|
Get distance to target Coordinate
|
||||||
|
|
||||||
:param target:
|
:param target:
|
||||||
:param mode: Calculation Mode (0 = Manhattan, 1 = Pythagoras)
|
:param mode: Calculation Mode (0 = Manhattan, 1 = Pythagoras)
|
||||||
:param includeDiagonals: in Manhattan Mode specify if diagonal movements are allowed (counts as 1.4)
|
:param includeDiagonals: in Manhattan Mode specify if diagonal
|
||||||
|
movements are allowed (counts as 1.4 in 2D, 1.7 in 3D)
|
||||||
:return: Distance to Target
|
:return: Distance to Target
|
||||||
"""
|
"""
|
||||||
assert isinstance(target, Coordinate)
|
assert isinstance(target, Coordinate)
|
||||||
assert mode in [0, 1]
|
assert isinstance(mode, DistanceAlgorithm)
|
||||||
assert isinstance(includeDiagonals, bool)
|
assert isinstance(includeDiagonals, bool)
|
||||||
|
|
||||||
if mode == 1:
|
if mode == DistanceAlgorithm.PYTHAGORAS:
|
||||||
return sqrt(abs(self.x - target.x) ** 2 + abs(self.y - target.y) ** 2)
|
if self.z is None:
|
||||||
elif mode == 0:
|
return sqrt(abs(self.x - target.x) ** 2 + abs(self.y - target.y) ** 2)
|
||||||
if not includeDiagonals:
|
|
||||||
return abs(self.x - target.x) + abs(self.y - target.y)
|
|
||||||
else:
|
else:
|
||||||
x_dist = abs(self.x - target.x)
|
ab = sqrt(abs(self.x - target.x) ** 2 + abs(self.y - target.y) ** 2)
|
||||||
y_dist = abs(self.y - target.y)
|
return sqrt(ab ** 2 + abs(self.z - target.z) ** 2)
|
||||||
o_dist = max(x_dist, y_dist) - min(x_dist, y_dist)
|
elif mode == DistanceAlgorithm.MANHATTAN:
|
||||||
return o_dist + 1.4 * min(x_dist, y_dist)
|
if not includeDiagonals:
|
||||||
|
if self.z is None:
|
||||||
|
return abs(self.x - target.x) + abs(self.y - target.y)
|
||||||
|
else:
|
||||||
|
return abs(self.x - target.x) + abs(self.y - target.y) + abs(self.z - target.z)
|
||||||
|
else:
|
||||||
|
dist = [abs(self.x - target.x), abs(self.y - target.y)]
|
||||||
|
if self.z is None:
|
||||||
|
o_dist = max(dist) - min(dist)
|
||||||
|
return o_dist + 1.4 * min(dist)
|
||||||
|
else:
|
||||||
|
dist.append(abs(self.z - target.z))
|
||||||
|
d_steps = min(dist)
|
||||||
|
dist.remove(min(dist))
|
||||||
|
dist = [x - d_steps for x in dist]
|
||||||
|
o_dist = max(dist) - min(dist)
|
||||||
|
return 1.7 * d_steps + o_dist + 1.4 * min(dist)
|
||||||
|
|
||||||
def getNeighbours(self, includeDiagonal: bool = True, minX: int = -inf, minY: int = -inf,
|
def getNeighbours(self, includeDiagonal: bool = True, minX: int = -inf, minY: int = -inf,
|
||||||
maxX: int = inf, maxY: int = inf) -> list[Coordinate]:
|
maxX: int = inf, maxY: int = inf, minZ: int = -inf, maxZ: int = inf) -> list[Coordinate]:
|
||||||
"""
|
"""
|
||||||
Get a list of neighbouring coordinates
|
Get a list of neighbouring coordinates
|
||||||
|
|
||||||
:param includeDiagonal: include diagonal neighbours
|
:param includeDiagonal: include diagonal neighbours
|
||||||
:param minX: ignore all neighbours that would have an X value below this
|
:param minX: ignore all neighbours that would have an X value below this
|
||||||
:param minY: ignore all neighbours that would have an Y value below this
|
:param minY: ignore all neighbours that would have an Y value below this
|
||||||
|
:param minZ: ignore all neighbours that would have an Z value below this
|
||||||
:param maxX: ignore all neighbours that would have an X value above this
|
:param maxX: ignore all neighbours that would have an X value above this
|
||||||
:param maxY: ignore all neighbours that would have an Y value above this
|
:param maxY: ignore all neighbours that would have an Y value above this
|
||||||
|
:param maxZ: ignore all neighbours that would have an Z value above this
|
||||||
:return: list of Coordinate
|
:return: list of Coordinate
|
||||||
"""
|
"""
|
||||||
neighbourList = []
|
neighbourList = []
|
||||||
if includeDiagonal:
|
if self.z is None:
|
||||||
nb_list = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
|
if includeDiagonal:
|
||||||
|
nb_list = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
|
||||||
|
else:
|
||||||
|
nb_list = [(-1, 0), (1, 0), (0, -1), (0, 1)]
|
||||||
|
|
||||||
|
for dx, dy in nb_list:
|
||||||
|
tx = self.x + dx
|
||||||
|
ty = self.y + dy
|
||||||
|
if minX <= tx <= maxX and minY <= ty <= maxY:
|
||||||
|
neighbourList.append(Coordinate(tx, ty))
|
||||||
else:
|
else:
|
||||||
nb_list = [(-1, 0), (1, 0), (0, -1), (0, 1)]
|
if includeDiagonal:
|
||||||
|
nb_list = [(x, y, z) for x in [-1, 0, 1] for y in [-1, 0, 1] for z in [-1, 0, 1]]
|
||||||
|
nb_list.remove((0, 0, 0))
|
||||||
|
else:
|
||||||
|
nb_list = [
|
||||||
|
(-1, 0, 0), (0, -1, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1), (0, 0, -1)
|
||||||
|
]
|
||||||
|
|
||||||
for dx, dy in nb_list:
|
for dx, dy, dz in nb_list:
|
||||||
tx = self.x + dx
|
tx = self.x + dx
|
||||||
ty = self.y + dy
|
ty = self.y + dy
|
||||||
if tx < minX or tx > maxX or ty < minY or ty > maxY:
|
tz = self.z + dz
|
||||||
continue
|
if minX <= tx <= maxX and minY <= ty <= maxY and minZ <= tz <= maxZ:
|
||||||
|
neighbourList.append(Coordinate(tx, ty, tz))
|
||||||
neighbourList.append(Coordinate(tx, ty))
|
|
||||||
|
|
||||||
return neighbourList
|
return neighbourList
|
||||||
|
|
||||||
def getAngleTo(self, target: Coordinate, normalized: bool = False) -> float:
|
def getAngleTo(self, target: Coordinate, normalized: bool = False) -> float:
|
||||||
"""normalized returns an angle going clockwise with 0 starting in the 'north'"""
|
"""normalized returns an angle going clockwise with 0 starting in the 'north'"""
|
||||||
|
if self.z is not None:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
dx = target.x - self.x
|
dx = target.x - self.x
|
||||||
dy = target.y - self.y
|
dy = target.y - self.y
|
||||||
if not normalized:
|
if not normalized:
|
||||||
@ -75,13 +117,17 @@ class Coordinate:
|
|||||||
return 180.0 + abs(angle)
|
return 180.0 + abs(angle)
|
||||||
|
|
||||||
def __add__(self, other: Coordinate) -> Coordinate:
|
def __add__(self, other: Coordinate) -> Coordinate:
|
||||||
return Coordinate(self.x + other.x, self.y + other.y)
|
if self.z is None:
|
||||||
|
return Coordinate(self.x + other.x, self.y + other.y)
|
||||||
|
else:
|
||||||
|
return Coordinate(self.x + other.x, self.y + other.y, self.z + other.z)
|
||||||
|
|
||||||
def __sub__(self, other: Coordinate) -> Coordinate:
|
def __sub__(self, other: Coordinate) -> Coordinate:
|
||||||
return Coordinate(self.x - other.x, self.y - other.y)
|
if self.z is None:
|
||||||
|
return Coordinate(self.x - other.x, self.y - other.y)
|
||||||
|
else:
|
||||||
|
return Coordinate(self.x - other.x, self.y - other.y, self.z - other.z)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def generate(from_x: int, to_x: int, from_y: int, to_y: int) -> List[Coordinate]:
|
def generate(from_x: int, to_x: int, from_y: int, to_y: int) -> List[Coordinate]:
|
||||||
return [Coordinate(x, y) for x in range(from_x, to_x + 1) for y in range(from_y, to_y + 1)]
|
return [Coordinate(x, y) for x in range(from_x, to_x + 1) for y in range(from_y, to_y + 1)]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user